Browse Source

python: add remaining vector/scalar, exp and other math functions.

Except for binomial coefficient, there the asserts are hard to replicate
and would need a change on Magnum side.
pull/15/head
Vladimír Vondruš 4 years ago
parent
commit
0120b3f768
  1. 4
      doc/python/magnum.math.rst
  2. 2
      doc/python/pages/changelog.rst
  3. 83
      src/python/magnum/math.cpp
  4. 47
      src/python/magnum/math.vector.h
  5. 29
      src/python/magnum/math.vectorfloat.cpp
  6. 7
      src/python/magnum/math.vectorintegral.cpp
  7. 104
      src/python/magnum/test/test_math.py

4
doc/python/magnum.math.rst

@ -195,6 +195,10 @@
- :dox:`Math::Matrix3::from()` / :dox:`Math::Matrix4::from()` are named
:ref:`Matrix3.from_()` / :ref:`Matrix4.from_()` because :py:`from` is
a Python keyword and thus can't be used as a name.
- :dox:`Math::isInf()` and :dox:`Math::isNan()` are named
:ref:`math.isinf() <magnum.math.isinf()>` and
:ref:`math.isnan() <magnum.math.isnan()>` for consistency with the
Python :ref:`math` module
- :cpp:`Math::gather()` and :cpp:`Math::scatter()` operations are
implemented as real swizzles:

2
doc/python/pages/changelog.rst

@ -44,6 +44,8 @@ Changelog
- Exposed newly added off-center variants of
:ref:`Matrix4.orthographic_projection()` and
:ref:`Matrix3.projection()`
- Exposed remaining vector/scalar, exponential and other functions in the
:ref:`math <magnum.math>` library
- Exposed :ref:`gl.Context` and its platform-specific subclasses for EGL, WGL
and GLX
- Exposed :ref:`gl.Renderer.set_blend_function()`,

83
src/python/magnum/math.cpp

@ -89,7 +89,7 @@ const Py_ssize_t MatrixStridesDouble[][2]{
namespace {
template<class T> void angle(py::class_<T>& c) {
template<class T> void angle(py::module_& m, py::class_<T>& c) {
/*
Missing APIs:
@ -153,9 +153,28 @@ template<class T> void angle(py::class_<T>& c) {
}, "Ratio of two values")
.def("__repr__", repr<T>, "Object representation");
/* Overloads of scalar functions */
m
.def("isinf", static_cast<bool(*)(T)>(Math::isInf), "If given number is a positive or negative infinity")
.def("isnan", static_cast<bool(*)(T)>(Math::isNan), "If given number is a NaN")
.def("min", static_cast<T(*)(T, T)>(Math::min), "Minimum", py::arg("value"), py::arg("min"))
.def("max", static_cast<T(*)(T, T)>(Math::max), "Maximum", py::arg("value"), py::arg("min"))
.def("minmax", static_cast<std::pair<T, T>(*)(T, T)>(Math::minmax), "Minimum and maximum of two values")
.def("clamp", static_cast<T(*)(T, T, T)>(Math::clamp), "Clamp value", py::arg("value"), py::arg("min"), py::arg("max"))
.def("sign", Math::sign<T>, "Sign")
.def("abs", static_cast<T(*)(T)>(Math::abs), "Absolute value")
.def("floor", static_cast<T(*)(T)>(Math::floor), "Nearest not larger integer")
.def("round", static_cast<T(*)(T)>(Math::round), "Round value to nearest integer")
.def("ceil", static_cast<T(*)(T)>(Math::ceil), "Nearest not smaller integer")
.def("fmod", static_cast<T(*)(T, T)>(Math::fmod), "Floating point division remainder")
.def("lerp", static_cast<T(*)(const T&, const T&, Double)>(Math::lerp), "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("lerp", static_cast<T(*)(const T&, const T&, bool)>(Math::lerp), "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("lerp_inverted", static_cast<Double(*)(T, T, T)>(Math::lerpInverted), "Inverse linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("lerp"))
.def("select", static_cast<T(*)(const T&, const T&, Double)>(Math::select), "Constant interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"));
}
template<class T> void boolVector(py::class_<T>& c) {
template<class T> void boolVector(py::module_& m, py::class_<T>& c) {
c
/* Constructors */
.def_static("zero_init", []() {
@ -230,6 +249,9 @@ template<class T> void boolVector(py::class_<T>& c) {
char lenDocstring[] = "Vector size. Returns _.";
lenDocstring[sizeof(lenDocstring) - 3] = '0' + T::Size;
c.def_static("__len__", []() { return int(T::Size); }, lenDocstring);
m
.def("lerp", Math::lerp<T::Size>, "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"));
}
template<class U, class T, class ...Args> void convertible(py::class_<T, Args...>& c) {
@ -418,8 +440,8 @@ void math(py::module_& root, py::module_& m) {
py::class_<Radd> rad{root, "Rad", "Radians"};
deg.def(py::init<Radd>(), "Conversion from radians");
rad.def(py::init<Degd>(), "Conversion from degrees");
angle(deg);
angle(rad);
angle(m, deg);
angle(m, rad);
/* Cyclic convertibility, so can't do that in angle() */
py::implicitly_convertible<Radd, Degd>();
@ -429,9 +451,9 @@ void math(py::module_& root, py::module_& m) {
py::class_<Math::BoolVector<2>> boolVector2{root, "BoolVector2", "Two-component bool vector"};
py::class_<Math::BoolVector<3>> boolVector3{root, "BoolVector3", "Three-component bool vector"};
py::class_<Math::BoolVector<4>> boolVector4{root, "BoolVector4", "Four-component bool vector"};
boolVector(boolVector2);
boolVector(boolVector3);
boolVector(boolVector4);
boolVector(m, boolVector2);
boolVector(m, boolVector3);
boolVector(m, boolVector4);
/* Constants. Putting them into math like Python does and as doubles, since
Python doesn't really differentiate between 32bit and 64bit floats */
@ -448,6 +470,12 @@ void math(py::module_& root, py::module_& m) {
/* Functions */
m
.def("div", [](Long x, Long y) { return Math::div(x, y); }, "Integer division with remainder", py::arg("x"), py::arg("y"))
/** @todo binomialCoefficient(), asserts are hard to replicate (have an
internal variant returning an Optional?) */
.def("popcount", static_cast<UnsignedInt(*)(UnsignedLong)>(Math::popcount), "Count of bits set in a number")
/* Trigonometry */
.def("sin", [](Radd angle) { return Math::sin(angle); }, "Sine")
.def("cos", [](Radd angle) { return Math::cos(angle); }, "Cosine")
.def("sincos", [](Radd angle) {
@ -456,7 +484,46 @@ void math(py::module_& root, py::module_& m) {
.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");
.def("atan", [](Double angle) { return Math::atan(angle); }, "Arc tangent")
/* Scalar/vector functions, scalar versions. Vector versions defined
for each vector variant below; angle versions defined above. */
.def("isinf", static_cast<bool(*)(Double)>(Math::isInf), "If given number is a positive or negative infinity")
.def("isnan", static_cast<bool(*)(Double)>(Math::isNan), "If given number is a NaN")
.def("min", static_cast<Long(*)(Long, Long)>(Math::min), "Minimum", py::arg("value"), py::arg("min"))
.def("min", static_cast<Double(*)(Double, Double)>(Math::min), "Minimum", py::arg("value"), py::arg("min"))
.def("max", static_cast<Long(*)(Long, Long)>(Math::max), "Maximum", py::arg("value"), py::arg("min"))
.def("max", static_cast<Double(*)(Double, Double)>(Math::max), "Maximum", py::arg("value"), py::arg("min"))
.def("minmax", static_cast<std::pair<Long, Long>(*)(Long, Long)>(Math::minmax), "Minimum and maximum of two values")
.def("minmax", static_cast<std::pair<Double, Double>(*)(Double, Double)>(Math::minmax), "Minimum and maximum of two values")
.def("clamp", static_cast<Long(*)(Long, Long, Long)>(Math::clamp), "Clamp value", py::arg("value"), py::arg("min"), py::arg("max"))
.def("clamp", static_cast<Double(*)(Double, Double, Double)>(Math::clamp), "Clamp value", py::arg("value"), py::arg("min"), py::arg("max"))
.def("sign", Math::sign<Long>, "Sign")
.def("sign", Math::sign<Double>, "Sign")
.def("abs", static_cast<Long(*)(Long)>(Math::abs), "Absolute value")
.def("abs", static_cast<Double(*)(Double)>(Math::abs), "Absolute value")
.def("floor", static_cast<Double(*)(Double)>(Math::floor), "Nearest not larger integer")
.def("round", static_cast<Double(*)(Double)>(Math::round), "Round value to nearest integer")
.def("ceil", static_cast<Double(*)(Double)>(Math::ceil), "Nearest not smaller integer")
.def("fmod", static_cast<Double(*)(Double, Double)>(Math::fmod), "Floating point division remainder")
.def("lerp", static_cast<Long(*)(const Long&, const Long&, Double)>(Math::lerp), "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("lerp", static_cast<Double(*)(const Double&, const Double&, Double)>(Math::lerp), "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("lerp", static_cast<Long(*)(const Long&, const Long&, bool)>(Math::lerp), "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("lerp", static_cast<Double(*)(const Double&, const Double&, bool)>(Math::lerp), "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("lerp_inverted", static_cast<Double(*)(Double, Double, Double)>(Math::lerpInverted), "Inverse linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("lerp"))
.def("select", static_cast<Long(*)(const Long&, const Long&, Double)>(Math::select), "Constant interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("select", static_cast<Double(*)(const Double&, const Double&, Double)>(Math::select), "Constant interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("fma", static_cast<Double(*)(Double, Double, Double)>(Math::fma), "Fused multiply-add")
/* Exponential and power. These are not defined for angles as they
require the type to be unitless. */
.def("log", static_cast<UnsignedInt(*)(UnsignedInt, UnsignedInt)>(Math::log), "Integral algorithm", py::arg("base"), py::arg("number"))
.def("log2", static_cast<UnsignedInt(*)(UnsignedInt)>(Math::log2), "Base-2 integral algorithm")
.def("log", static_cast<Double(*)(Double)>(Math::log), "Natural algorithm")
.def("exp", static_cast<Double(*)(Double)>(Math::exp), "Natural exponential")
.def("pow", static_cast<Double(*)(Double, Double)>(Math::pow), "Power")
.def("sqrt", static_cast<Double(*)(Double)>(Math::sqrt), "Square root")
.def("sqrt_inverted", static_cast<Double(*)(Double)>(Math::sqrtInverted), "Square root");
/* These are needed for the quaternion, so register them before. Double
versions are called from inside these. */

47
src/python/magnum/math.vector.h

@ -198,6 +198,42 @@ template<class T> void vector(py::module_& m, py::class_<T>& c) {
*/
m
/* Lambdas in order to convert from the generic Vector<size, T> */
.def("min", [](const T& value, const T& min) {
return T{Math::min(value, min)};
}, "Minimum", py::arg("value"), py::arg("min"))
.def("min", [](const T& value, typename T::Type min) {
return T{Math::min(value, min)};
}, "Minimum", py::arg("value"), py::arg("min"))
.def("max", [](const T& value, const T& max) {
return T{Math::max(value, max)};
}, "Maximum", py::arg("value"), py::arg("max"))
.def("max", [](const T& value, typename T::Type max) {
return T{Math::max(value, max)};
}, "Maximum", py::arg("value"), py::arg("max"))
.def("minmax", [](const T& a, const T& b) {
return std::pair<T, T>{Math::minmax(a, b)};
}, "Minimum and maximum of two values")
.def("clamp", [](const T& a, const T& min, const T& max) {
return T{Math::clamp(a, min, max)};
}, "Clamp value", py::arg("value"), py::arg("min"), py::arg("max"))
.def("clamp", [](const T& a, typename T::Type min, typename T::Type max) {
return T{Math::clamp(a, min, max)};
}, "Clamp value", py::arg("value"), py::arg("min"), py::arg("max"))
.def("lerp", [](const T& a, const T& b, Double t) {
return T{Math::lerp(a, b, t)};
}, "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
/* The BoolVector overload has to be before the bool to match first */
.def("lerp", [](const T& a, const T& b, Math::BoolVector<T::Size> t) {
return T{Math::lerp(a, b, t)};
}, "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("lerp", [](const T& a, const T& b, bool t) {
return T{Math::lerp(a, b, t)};
}, "Linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("select", [](const T& a, const T& b, Double t) {
return T{Math::select(a, b, t)};
}, "Constant interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("dot", [](const T& a, const T& b) { return Math::dot(a, b); },
"Dot product of two vectors");
@ -338,6 +374,17 @@ template<class T> void vector(py::module_& m, py::class_<T>& c) {
c.def_static("__len__", []() { return int(T::Size); }, lenDocstring);
}
/* Things common for vectors of all sizes and types */
template<class T> void vectorSigned(py::module_& m, py::class_<T>&) {
m
.def("sign", [](const T& a) {
return T{Math::sign(a)};
}, "Sign")
.def("abs", [](const T& a) {
return T{Math::abs(a)};
}, "Absolute value");
}
template<class T> void vector2(py::class_<Math::Vector2<T>>& c) {
py::implicitly_convertible<const std::tuple<T, T>&, Math::Vector2<T>>();

29
src/python/magnum/math.vectorfloat.cpp

@ -31,6 +31,32 @@ namespace {
template<class T> void vectorFloat(py::module_& m, py::class_<T>& c) {
m
/* Lambdas in order to convert to/from the generic Vector<size, T> */
.def("isinf", [](const T& a) {
return Math::isInf(a);
}, "If given number is a positive or negative infinity")
.def("isnan", [](const T& a) {
return Math::isNan(a);
}, "If given number is a NaN")
.def("floor", [](const T& a) {
return T{Math::floor(a)};
}, "Nearest not larger integer")
.def("round", [](const T& a) {
return T{Math::round(a)};
}, "Round value to nearest integer")
.def("ceil", [](const T& a) {
return T{Math::ceil(a)};
}, "Nearest not smaller integer")
.def("fmod", [](const T& a, const T& b) {
return T{Math::fmod(a, b)};
}, "Floating point division remainder")
.def("lerp_inverted", [](const T& a, const T& b, const T& t) {
return T{Math::lerpInverted(a, b, t)};
}, "Inverse linear interpolation of two values", py::arg("a"), py::arg("b"), py::arg("t"))
.def("fma", [](const T& a, const T& b, const T& c) {
return T{Math::fma(a, b, c)};
}, "Fused multiply-add")
.def("angle", [](const T& a, const T& b) { return Radd(Math::angle(a, b)); },
"Angle between normalized vectors", py::arg("normalized_a"), py::arg("normalized_b"));
@ -60,6 +86,7 @@ template<class T> void vectorsFloat(py::module_& m, py::class_<Math::Vector2<T>>
everyVector(vector2_);
everyVectorSigned(vector2_);
vector<Math::Vector2<T>>(m, vector2_);
vectorSigned<Math::Vector2<T>>(m, vector2_);
vectorFloat<Math::Vector2<T>>(m, vector2_);
vector2<T>(vector2_);
vector2Signed<T>(vector2_);
@ -69,12 +96,14 @@ template<class T> void vectorsFloat(py::module_& m, py::class_<Math::Vector2<T>>
everyVector(vector3_);
everyVectorSigned(vector3_);
vector<Math::Vector3<T>>(m, vector3_);
vectorSigned<Math::Vector3<T>>(m, vector3_);
vectorFloat<Math::Vector3<T>>(m, vector3_);
vector3<T>(vector3_);
everyVector(vector4_);
everyVectorSigned(vector4_);
vector<Math::Vector4<T>>(m, vector4_);
vectorSigned<Math::Vector4<T>>(m, vector4_);
vectorFloat<Math::Vector4<T>>(m, vector4_);
vector4<T>(vector4_);
}

7
src/python/magnum/math.vectorintegral.cpp

@ -98,10 +98,13 @@ template<class T> void vectorsIntegral(py::module_& m, py::class_<Math::Vector2<
vector4<T>(vector4_);
}
template<class T> void vectorsIntegralSigned(py::class_<Math::Vector2<T>>& vector2_, py::class_<Math::Vector3<T>>& vector3_, py::class_<Math::Vector4<T>>& vector4_) {
template<class T> void vectorsIntegralSigned(py::module_& m, py::class_<Math::Vector2<T>>& vector2_, py::class_<Math::Vector3<T>>& vector3_, py::class_<Math::Vector4<T>>& vector4_) {
everyVectorSigned(vector2_);
everyVectorSigned(vector3_);
everyVectorSigned(vector4_);
vectorSigned(m, vector2_);
vectorSigned(m, vector3_);
vectorSigned(m, vector4_);
vector2Signed<T>(vector2_);
}
@ -138,7 +141,7 @@ void mathVectorIntegral(py::module_& root, py::module_& m) {
/* Now register the generic from-list constructors and everything else */
vectorsIntegral<Int>(m, vector2i, vector3i, vector4i);
vectorsIntegralSigned<Int>(vector2i, vector3i, vector4i);
vectorsIntegralSigned<Int>(m, vector2i, vector3i, vector4i);
vectorsIntegral<UnsignedInt>(m, vector2ui, vector3ui, vector4ui);
}

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

@ -110,6 +110,10 @@ class Constants(unittest.TestCase):
class Functions(unittest.TestCase):
def test(self):
self.assertEqual(math.div(16, 5), (3, 1))
self.assertEqual(math.popcount(0xb5d194), 12)
def test_trigonometry(self):
self.assertAlmostEqual(math.sin(Deg(45.0)), 0.7071067811865475)
self.assertAlmostEqual(Deg(math.asin(0.7071067811865475)), Deg(45.0))
@ -117,6 +121,106 @@ class Functions(unittest.TestCase):
self.assertAlmostEqual(sincos[0], 1.0)
self.assertAlmostEqual(sincos[1], 0.0)
def test_scalar(self):
self.assertFalse(math.isinf(math.nan))
self.assertFalse(math.isnan(math.inf))
self.assertTrue(math.isinf(math.inf))
self.assertTrue(math.isnan(math.nan))
self.assertEqual(math.min(15.0, 3.0), 3.0)
self.assertEqual(math.max(15.0, 3.0), 15.0)
self.assertEqual(math.minmax(15.0, 3.0), (3.0, 15.0))
self.assertEqual(math.clamp(0.5, -1.0, 5.0), 0.5)
self.assertEqual(math.sign(-15.0), -1.0)
self.assertEqual(math.abs(-15.0), 15.0)
self.assertEqual(math.floor(15.3), 15.0)
self.assertEqual(math.ceil(15.3), 16.0)
self.assertEqual(math.round(15.3), 15.0)
self.assertEqual(math.round(15.7), 16.0)
self.assertAlmostEqual(math.fmod(5.1, 3.0), 2.1)
self.assertEqual(math.lerp(2.0, 5.0, 0.5), 3.5)
self.assertEqual(math.lerp(2.0, 5.0, False), 2.0)
self.assertEqual(math.lerp(2, 5, 0.5), 3)
self.assertEqual(math.lerp(2, 5, True), 5)
self.assertEqual(math.lerp_inverted(2.0, 5.0, 3.5), 0.5)
self.assertEqual(math.select(2.0, 5.0, 0.6), 2.0)
self.assertEqual(math.select(2, 5, 1.0), 5)
self.assertEqual(math.fma(2.0, 3.0, 0.75), 6.75)
def test_scalar_angle(self):
self.assertFalse(math.isinf(Deg(math.nan)))
self.assertFalse(math.isnan(Rad(math.inf)))
self.assertTrue(math.isinf(Deg(math.inf)))
self.assertTrue(math.isnan(Rad(math.nan)))
self.assertEqual(math.min(Deg(15.0), Deg(3.0)), Deg(3.0))
self.assertEqual(math.max(Rad(15.0), Rad(3.0)), Rad(15.0))
self.assertEqual(math.minmax(Deg(15.0), Deg(3.0)), (Deg(3.0), Deg(15.0)))
self.assertEqual(math.clamp(Rad(0.5), Rad(-1.0), Rad(5.0)), Rad(0.5))
self.assertEqual(math.sign(Deg(-15.0)), Deg(-1.0))
self.assertEqual(math.abs(Rad(-15.0)), Rad(15.0))
self.assertEqual(math.floor(Deg(15.3)), Deg(15.0))
self.assertEqual(math.ceil(Rad(15.3)), Rad(16.0))
self.assertEqual(math.round(Deg(15.3)), Deg(15.0))
self.assertEqual(math.round(Rad(15.7)), Rad(16.0))
self.assertAlmostEqual(math.fmod(Deg(5.1), Deg(3.0)), Deg(2.1))
self.assertEqual(math.lerp(Deg(2.0), Deg(5.0), 0.5), Deg(3.5))
self.assertEqual(math.lerp(Rad(2.0), Rad(5.0), False), Rad(2.0))
self.assertEqual(math.lerp_inverted(Deg(2.0), Deg(5.0), Deg(3.5)), 0.5)
self.assertEqual(math.select(Rad(2.0), Rad(5.0), 0.6), Rad(2.0))
def test_vector(self):
self.assertEqual(math.isinf((math.inf, math.nan)), BoolVector2(0b01))
self.assertEqual(math.isnan((math.inf, math.nan)), BoolVector2(0b10))
self.assertEqual(math.min((15.0, 0.5), (3.0, 1.0)), (3.0, 0.5))
self.assertEqual(math.min((15.0, 0.5), 3.0), (3.0, 0.5))
self.assertEqual(math.max((15.0, 0.5), (3.0, 1.0)), (15.0, 1.0))
self.assertEqual(math.max((15.0, 0.5), 3.0), (15.0, 3.0))
self.assertEqual(math.minmax((15.0, 0.5), (3.0, 1.0)), ((3.0, 0.5), (15.0, 1.0)))
self.assertEqual(math.clamp((0.5, 3.5), (-1.0, 1.0), (5.0, 2.0)), (0.5, 2.0))
self.assertEqual(math.clamp((0.5, 3.5), -1.0, 1.0), (0.5, 1.0))
self.assertEqual(math.sign((-15.0, 15.0)), (-1.0, 1.0))
self.assertEqual(math.abs((-15.0, 15.0)), (15.0, 15.0))
self.assertEqual(math.floor((15.3, 15.6)), (15.0, 15.0))
self.assertEqual(math.ceil((15.3, 15.6)), (16.0, 16.0))
self.assertEqual(math.round((15.3, 15.6)), (15.0, 16.0))
self.assertEqual(math.fmod((5.1, 1.5), (3.0, 1.0)), (2.1, 0.5))
self.assertEqual(math.lerp((2.0, 1.0), (5.0, 2.0), 0.5), (3.5, 1.5))
self.assertEqual(math.lerp((2.0, 1.0), (5.0, 2.0), BoolVector2(0b01)), (5.0, 1.0))
self.assertEqual(math.lerp((2.0, 1.0), (5.0, 2.0), False), (2.0, 1.0))
self.assertEqual(math.lerp((2, 1), (5, 2), 0.5), (3, 1))
self.assertEqual(math.lerp((2, 1), (5, 2), BoolVector2(0b01)), (5, 1))
self.assertEqual(math.lerp((2, 1), (5, 2), True), (5, 2))
self.assertEqual(math.lerp(BoolVector4(0b1001), BoolVector4(0b0110), BoolVector4(0b0101)), BoolVector4(0b1100))
self.assertEqual(math.lerp_inverted((2.0, 1.0), (5.0, 2.0), (3.5, 1.5)), (0.5, 0.5))
self.assertEqual(math.select((2.0, 1.0), (5.0, 2.0), 0.6), (2.0, 1.0))
self.assertEqual(math.select((2, 1), (5, 2), 1.0), (5, 2))
self.assertEqual(math.fma((2.0, 1.0), (3.0, 2.0), (0.75, 0.1)), (6.75, 2.1))
def test_exponential(self):
self.assertEqual(math.log(2, 256), 8)
self.assertEqual(math.log2(256), 8)
self.assertAlmostEqual(math.log(2.0), 0.69314718)
self.assertAlmostEqual(math.exp(0.69314718), 2.0)
self.assertAlmostEqual(math.pow(2.0, 0.5), 1.414213562)
self.assertAlmostEqual(math.sqrt(2.0), 1.414213562)
self.assertAlmostEqual(math.sqrt_inverted(2.0), 1/1.414213562)
class Vector(unittest.TestCase):
def test_init(self):
a = Vector4i()

Loading…
Cancel
Save