/* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Vladimír Vondruš Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include "magnum/bootstrap.h" #include "magnum/math.h" namespace magnum { namespace { template void angle(py::class_& c) { /* Missing APIs: Type */ c /* Constructors */ .def_static("zero_init", []() { return T{Math::ZeroInit}; }, "Construct a zero value") .def(py::init(), "Default constructor") .def(py::init(), "Explicit conversion from a unitless type") /* Explicit conversion to an underlying type */ .def("__float__", &T::operator typename T::Type, "Conversion to underlying type") /* Comparison */ .def(py::self == py::self, "Equality comparison") .def(py::self != py::self, "Non-equality comparison") .def(py::self < py::self, "Less than comparison") .def(py::self > py::self, "Greater than comparison") .def(py::self <= py::self, "Less than or equal comparison") .def(py::self >= py::self, "Greater than or equal comparison") /* Arithmetic ops. Need to use lambdas because the C++ functions return the Unit base class :( */ .def("__neg__", [](const T& self) -> T { return -self; }, "Negated value") .def("__iadd__", [](T& self, const T& other) -> T& { self += other; return self; }, "Add and assign a value") .def("__add__", [](const T& self, const T& other) -> T { return self + other; }, "Add a value") .def("__isub__", [](T& self, const T& other) -> T& { self -= other; return self; }, "Subtract and assign a value") .def("__sub__", [](const T& self, const T& other) -> T { return self - other; }, "Subtract a value") .def("__imul__", [](T& self, typename T::Type other) -> T& { self *= other; return self; }, "Multiply with a number and assign") .def("__mul__", [](const T& self, typename T::Type other) -> T { return self * other; }, "Multiply with a number") .def("__itruediv__", [](T& self, typename T::Type other) -> T& { self /= other; return self; }, "Divide with a number and assign") .def("__truediv__", [](const T& self, typename T::Type other) -> T { return self / other; }, "Divide with a number") .def("__truediv__", [](const T& self, const T& other) -> typename T::Type { return self / other; }, "Ratio of two values") .def("__repr__", repr, "Object representation"); } template void boolVector(py::class_& c) { c /* Constructors */ .def_static("zero_init", []() { return T{Math::ZeroInit}; }, "Construct a zero-filled boolean vector") .def(py::init(), "Default constructor") .def(py::init(), "Construct a boolean vector with one value for all fields") .def(py::init(), "Construct a boolean vector from segment values") /* Explicit conversion to bool */ .def("__bool__", &T::operator bool, "Boolean conversion") /* Comparison */ .def(py::self == py::self, "Equality comparison") .def(py::self != py::self, "Non-equality comparison") /* Member functions */ .def("all", &T::all, "Whether all bits are set") .def("none", &T::none, "Whether no bits are set") .def("any", &T::any, "Whether any bit is set") /* Set / get. Need to throw IndexError in order to allow iteration: https://docs.python.org/3/reference/datamodel.html#object.__getitem__ */ .def("__setitem__",[](T& self, std::size_t i, bool value) { if(i >= T::Size) throw pybind11::index_error{}; self.set(i, value); }, "Set a bit at given position") .def("__getitem__", [](const T& self, std::size_t i) { if(i >= T::Size) throw pybind11::index_error{}; return self[i]; }, "Bit at given position") /* Operators */ .def(~py::self, "Bitwise inversion") .def(py::self &= py::self, "Bitwise AND and assign") .def(py::self & py::self, "Bitwise AND") .def(py::self |= py::self, "Bitwise OR and assign") .def(py::self | py::self, "Bitwise OR") .def(py::self ^= py::self, "Bitwise XOR and assign") .def(py::self ^ py::self, "Bitwise XOR") .def("__repr__", repr, "Object representation"); /* Vector length */ char lenDocstring[] = "Vector size. Returns _."; lenDocstring[sizeof(lenDocstring) - 3] = '0' + T::Size; c.def_static("__len__", []() { return int(T::Size); }, lenDocstring); } template void quaternion(py::module& m, py::class_& c) { /* Missing APIs: Type construction from different types */ m .def("dot", static_cast(&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(&Math::lerp), "Linear interpolation of two quaternions", py::arg("normalized_a"), py::arg("normalized_b"), py::arg("t")) .def("lerp_shortest_path", static_cast(&Math::lerpShortestPath), "Linear shortest-path interpolation of two quaternions", py::arg("normalized_a"), py::arg("normalized_b"), py::arg("t")) .def("slerp", static_cast(&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(&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& axis) { return T::rotation(Math::Rad(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&, typename T::Type>(), "Construct from a vector and a scalar") .def(py::init([](const std::pair, 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&>(), "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(T::*)() const>(&T::vector), [](T& self, const Math::Vector3& value) { self.vector() = value; }, "Vector part") .def_property("scalar", static_cast(&T::scalar), [](T& self, typename T::Type value) { self.scalar() = value; }, "Scalar part") .def("__repr__", repr, "Object representation"); } } void math(py::module& root, py::module& m) { m.doc() = "Math library"; /* Deg, Rad, Degd, Radd */ py::class_ deg{root, "Deg", "Degrees"}; py::class_ rad{root, "Rad", "Radians"}; deg.def(py::init(), "Conversion from radians"); rad.def(py::init(), "Conversion from degrees"); angle(deg); angle(rad); /* Cyclic convertibility, so can't do that in angle() */ py::implicitly_convertible(); py::implicitly_convertible(); /* BoolVector */ py::class_> boolVector2{root, "BoolVector2", "Two-component bool vector"}; py::class_> boolVector3{root, "BoolVector3", "Three-component bool vector"}; py::class_> boolVector4{root, "BoolVector4", "Four-component bool vector"}; boolVector(boolVector2); boolVector(boolVector3); boolVector(boolVector4); /* Constants. Putting them into math like Python does and as doubles, since Python doesn't really differentiate between 32bit and 64bit floats */ m.attr("pi") = Constantsd::pi(); m.attr("pi_half") = Constantsd::piHalf(); m.attr("pi_quarter") = Constantsd::piQuarter(); m.attr("tau") = Constantsd::tau(); m.attr("e") = Constantsd::e(); m.attr("sqrt2") = Constantsd::sqrt2(); m.attr("sqrt3") = Constantsd::sqrt3(); m.attr("sqrt_half") = Constantsd::sqrtHalf(); m.attr("nan") = Constantsd::nan(); m.attr("inf") = Constantsd::inf(); /* Functions */ m .def("sin", [](Radd angle) { return Math::sin(angle); }, "Sine") .def("cos", [](Radd angle) { return Math::cos(angle); }, "Cosine") .def("sincos", [](Radd angle) { return Math::sincos(angle); }, "Sine and cosine") .def("tan", [](Radd angle) { return Math::tan(angle); }, "Tangent") .def("asin", [](Double angle) { return Math::asin(angle); }, "Arc sine") .def("acos", [](Double angle) { return Math::acos(angle); }, "Arc cosine") .def("atan", [](Double angle) { return Math::atan(angle); }, "Arc tangent"); /* 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_(root, "Quaternion", "Float quaternion"); py::class_ quaterniond(root, "Quaterniond", "Double quaternion"); quaternion(m, quaternion_); quaternion(m, quaterniond); } }