Browse Source

python: expose Quaternion.

pull/1/head
Vladimír Vondruš 7 years ago
parent
commit
a55a7e6009
  1. 2
      src/python/magnum/__init__.py
  2. 4
      src/python/magnum/magnum.cpp
  3. 117
      src/python/magnum/math.cpp
  4. 50
      src/python/magnum/test/test_math.py

2
src/python/magnum/__init__.py

@ -43,4 +43,6 @@ __all__ = [
'Matrix3x2d', 'Matrix3x3d', 'Matrix3x4d',
'Matrix4x2d', 'Matrix4x3d', 'Matrix4x4d',
'Matrix3', 'Matrix4', 'Matrix3d', 'Matrix4d',
'Quaternion', 'Quaterniond'
]

4
src/python/magnum/magnum.cpp

@ -34,8 +34,4 @@ PYBIND11_MODULE(_magnum, m) {
py::module math = m.def_submodule("math");
magnum::math(m, math);
magnum::mathVectorFloat(m, math);
magnum::mathVectorIntegral(m, math);
magnum::mathMatrixFloat(m);
magnum::mathMatrixDouble(m);
}

117
src/python/magnum/math.cpp

@ -29,6 +29,7 @@
#include <Magnum/Magnum.h>
#include <Magnum/Math/Angle.h>
#include <Magnum/Math/BoolVector.h>
#include <Magnum/Math/Quaternion.h>
#include "magnum/bootstrap.h"
#include "magnum/math.h"
@ -153,6 +154,110 @@ template<class T> void boolVector(py::class_<T>& c) {
c.def_static("__len__", []() { return int(T::Size); }, lenDocstring);
}
template<class T> void quaternion(py::module& m, py::class_<T>& c) {
/*
Missing APIs:
Type
construction from different types
*/
m
.def("dot", static_cast<typename T::Type(*)(const T&, const T&)>(&Math::dot),
"Dot product between two quaternions")
.def("angle", [](const T& a, const T& b) {
return Radd(Math::angle(a, b));
}, "Angle between normalized quaternions")
.def("lerp", static_cast<T(*)(const T&, const T&, typename T::Type)>(&Math::lerp),
"Linear interpolation of two quaternions", py::arg("normalized_a"), py::arg("normalized_b"), py::arg("t"))
.def("lerp_shortest_path", static_cast<T(*)(const T&, const T&, typename T::Type)>(&Math::lerpShortestPath),
"Linear shortest-path interpolation of two quaternions", py::arg("normalized_a"), py::arg("normalized_b"), py::arg("t"))
.def("slerp", static_cast<T(*)(const T&, const T&, typename T::Type)>(&Math::slerp),
"Spherical linear interpolation of two quaternions", py::arg("normalized_a"), py::arg("normalized_b"), py::arg("t"))
.def("slerp_shortest_path", static_cast<T(*)(const T&, const T&, typename T::Type)>(&Math::slerpShortestPath),
"Spherical linear shortest-path interpolation of two quaternions", py::arg("normalized_a"), py::arg("normalized_b"), py::arg("t"))
;
c
/* Constructors */
.def_static("rotation", [](Radd angle, const Math::Vector3<typename T::Type>& axis) {
return T::rotation(Math::Rad<typename T::Type>(angle), axis);
}, "Rotation quaternion")
.def_static("from_matrix", &T::fromMatrix,
"Create a quaternion from rotation matrix")
.def_static("zero_init", []() {
return T{Math::ZeroInit};
}, "Construct a zero-initialized quaternion")
.def_static("identity_init", []() {
return T{Math::IdentityInit};
}, "Construct an identity quaternion")
.def(py::init(), "Default constructor")
.def(py::init<const Math::Vector3<typename T::Type>&, typename T::Type>(),
"Construct from a vector and a scalar")
.def(py::init([](const std::pair<std::tuple<typename T::Type, typename T::Type, typename T::Type>, typename T::Type>& value) {
return T{{std::get<0>(value.first), std::get<1>(value.first), std::get<2>(value.first)}, value.second};
}), "Construct from a tuple")
.def(py::init<const Math::Vector3<typename T::Type>&>(),
"Construct from a vector")
/* Comparison */
.def(py::self == py::self, "Equality comparison")
.def(py::self != py::self, "Non-equality comparison")
/* Operators */
.def(-py::self, "Negated quaternion")
.def(py::self += py::self, "Add and assign a quaternion")
.def(py::self + py::self, "Add a quaternion")
.def(py::self -= py::self, "Subtract and assign a quaternion")
.def(py::self - py::self, "Subtract a quaternion")
.def(py::self *= typename T::Type{}, "Multiply with a scalar and assign")
.def(py::self * typename T::Type{}, "Multiply with a scalar")
.def(py::self /= typename T::Type{}, "Divide with a scalar and assign")
.def(py::self / typename T::Type{}, "Divide with a scalar")
.def(py::self * py::self, "Multiply with a quaternion")
.def(typename T::Type{} * py::self, "Multiply a scalar with a quaternion")
.def(typename T::Type{} / py::self, "Divide a quaternion with a scalar and invert")
/* Member functions */
.def("is_normalized", &T::isNormalized,
"Whether the quaternion is normalized")
.def("angle", [](const T& self) {
return Radd(self.angle());
}, "Rotation angle of a unit quaternion")
.def("axis", &T::axis,
"Rotation axis of a unit quaternion")
.def("to_matrix", &T::toMatrix,
"Convert to a rotation matrix")
.def("dot", &T::dot,
"Dot product of the quaternion")
.def("length", &T::length,
"Quaternion length")
.def("normalized", &T::normalized,
"Normalized quaternion (of unit length)")
.def("conjugated", &T::conjugated,
"Conjugated quaternion")
.def("inverted", &T::inverted,
"Inverted quaternion")
.def("inverted_normalized", &T::invertedNormalized,
"Inverted normalized quaternion")
.def("transform_vector", &T::transformVector,
"Rotate a vector with a quaternion")
.def("transform_vector_normalized", &T::transformVectorNormalized,
"Rotate a vector with a normalized quaternion")
/* Properties */
.def_property("vector",
static_cast<const Math::Vector3<typename T::Type>(T::*)() const>(&T::vector),
[](T& self, const Math::Vector3<typename T::Type>& value) { self.vector() = value; },
"Vector part")
.def_property("scalar",
static_cast<typename T::Type(T::*)() const>(&T::scalar),
[](T& self, typename T::Type value) { self.scalar() = value; },
"Scalar part")
.def("__repr__", repr<T>, "Object representation");
}
}
void math(py::module& root, py::module& m) {
@ -190,6 +295,18 @@ void math(py::module& root, py::module& m) {
m.attr("sqrt_half") = Constantsd::sqrtHalf();
m.attr("nan") = Constantsd::nan();
m.attr("inf") = Constantsd::inf();
/* These are needed for the quaternion, so register them before */
magnum::mathVectorFloat(root, m);
magnum::mathVectorIntegral(root, m);
magnum::mathMatrixFloat(root);
magnum::mathMatrixDouble(root);
/* Quaternion */
py::class_<Quaternion> quaternion_(root, "Quaternion", "Float quaternion");
py::class_<Quaterniond> quaterniond(root, "Quaterniond", "Double quaternion");
quaternion(m, quaternion_);
quaternion(m, quaterniond);
}
}

50
src/python/magnum/test/test_math.py

@ -448,3 +448,53 @@ class Matrix4_(unittest.TestCase):
def test_methods(self):
self.assertEqual(Matrix4.rotation_x(Deg(45.0)).inverted(),
Matrix4.rotation_x(Deg(-45.0)))
class Quaternion_(unittest.TestCase):
def test_init(self):
a = Quaternion()
self.assertEqual(a.vector, Vector3(0.0, 0.0, 0.0))
self.assertEqual(a.scalar, 1.0)
b = Quaternion.identity_init()
self.assertEqual(b.vector, Vector3(0.0, 0.0, 0.0))
self.assertEqual(b.scalar, 1.0)
c = Quaternion.zero_init()
self.assertEqual(c.vector, Vector3(0.0, 0.0, 0.0))
self.assertEqual(c.scalar, 0.0)
d = Quaternion(Vector3(1.0, 2.0, 3.0), 4.0)
self.assertEqual(d.vector, Vector3(1.0, 2.0, 3.0))
self.assertEqual(d.scalar, 4.0)
e = Quaternion(((1.0, 2.0, 3.0), 4.0))
self.assertEqual(e.vector, Vector3(1.0, 2.0, 3.0))
self.assertEqual(e.scalar, 4.0)
def test_static_methods(self):
a = Quaternion.rotation(Deg(45.0), Vector3.x_axis())
self.assertEqual(a, Quaternion((0.382683, 0.0, 0.0), 0.92388))
self.assertEqual(a.to_matrix(), Matrix4.rotation_x(Deg(45.0)).rotation_scaling())
def test_methods(self):
a = Quaternion.rotation(Deg(45.0), Vector3.x_axis())
self.assertEqual(a.inverted(),
Quaternion.rotation(Deg(45.0), -Vector3.x_axis()))
self.assertAlmostEqual(float(Deg(a.angle())), float(Deg(45.0)), 4)
def test_functions(self):
a = math.angle(Quaterniond.rotation(Deg(45.0), Vector3d.x_axis()),
Quaterniond.rotation(Deg(75.0), Vector3d.x_axis()))
self.assertEqual(Deg(a), Deg(15.0))
def test_properties(self):
a = Quaternion()
a.vector = (1.0, 2.0, 3.0)
a.scalar = 4.0
self.assertEqual(a.vector, Vector3(1.0, 2.0, 3.0))
self.assertEqual(a.scalar, 4.0)
self.assertEqual(a, Quaternion((1.0, 2.0, 3.0), 4.0))
def test_repr(self):
a = Quaternion.rotation(Deg(45.0), Vector3.x_axis())
self.assertEqual(repr(a), 'Quaternion({0.382683, 0, 0}, 0.92388)')

Loading…
Cancel
Save