Browse Source

python: make it possible to create matrices from nested tuples.

Ugh, I need to drop this std::tuple. It makes code complicated for no
reason.
pull/2/head
Vladimír Vondruš 7 years ago
parent
commit
452d9478ab
  1. 101
      src/python/magnum/math.matrix.h
  2. 179
      src/python/magnum/test/test_math.py

101
src/python/magnum/math.matrix.h

@ -181,6 +181,13 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector2<T>, Math::Vector2<T>>& value) {
return Math::Matrix2x2<T>{std::get<0>(value), std::get<1>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T>,
std::tuple<T, T>>& value) {
return Math::Matrix2x2<T>{
Math::Vector2<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value))},
Math::Vector2<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value))}
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix2x2<T>& self, const Math::Matrix3x2<T>& other) -> Math::Matrix3x2<T> {
return self*other;
}, "Multiply a matrix")
@ -193,6 +200,13 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector3<T>, Math::Vector3<T>>& value) {
return Math::Matrix2x3<T>{std::get<0>(value), std::get<1>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T, T>,
std::tuple<T, T, T>>& value) {
return Math::Matrix2x3<T>{
Math::Vector3<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value))},
Math::Vector3<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value))}
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix2x3<T>& self, const Math::Matrix2x2<T>& other) -> Math::Matrix2x3<T> {
return self*other;
}, "Multiply a matrix")
@ -211,6 +225,13 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector4<T>, Math::Vector4<T>>& value) {
return Math::Matrix2x4<T>{std::get<0>(value), std::get<1>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>>& value) {
return Math::Matrix2x4<T>{
Math::Vector4<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value)), std::get<3>(std::get<0>(value))},
Math::Vector4<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value)), std::get<3>(std::get<1>(value))}
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix2x4<T>& self, const Math::Matrix2x2<T>& other) -> Math::Matrix2x4<T> {
return self*other;
}, "Multiply a matrix")
@ -239,6 +260,15 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector2<T>, Math::Vector2<T>, Math::Vector2<T>>& value) {
return Math::Matrix3x2<T>{std::get<0>(value), std::get<1>(value), std::get<2>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T>,
std::tuple<T, T>,
std::tuple<T, T>>& value) {
return Math::Matrix3x2<T>{
Math::Vector2<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value))},
Math::Vector2<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value))},
Math::Vector2<T>{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value))}
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix3x2<T>& self, const Math::Matrix2x3<T>& other) -> Math::Matrix2x2<T> {
return self*other;
}, "Multiply a matrix")
@ -257,6 +287,15 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector3<T>, Math::Vector3<T>, Math::Vector3<T>>& value) {
return Math::Matrix3x3<T>{std::get<0>(value), std::get<1>(value), std::get<2>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T, T>,
std::tuple<T, T, T>,
std::tuple<T, T, T>>& value) {
return Math::Matrix3x3<T>{
Math::Vector3<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value))},
Math::Vector3<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value))},
Math::Vector3<T>{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value))}
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix3x3<T>& self, const Math::Matrix2x3<T>& other) -> Math::Matrix2x3<T> {
return self*other;
}, "Multiply a matrix")
@ -269,6 +308,15 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector4<T>, Math::Vector4<T>, Math::Vector4<T>>& value) {
return Math::Matrix3x4<T>{std::get<0>(value), std::get<1>(value), std::get<2>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>>& value) {
return Math::Matrix3x4<T>{
Math::Vector4<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value)), std::get<3>(std::get<0>(value))},
Math::Vector4<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value)), std::get<3>(std::get<1>(value))},
Math::Vector4<T>{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value)), std::get<3>(std::get<2>(value))}
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix3x4<T>& self, const Math::Matrix2x3<T>& other) -> Math::Matrix2x4<T> {
return self*other;
}, "Multiply a matrix")
@ -297,6 +345,17 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector2<T>, Math::Vector2<T>, Math::Vector2<T>, Math::Vector2<T>>& value) {
return Math::Matrix4x2<T>{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T>,
std::tuple<T, T>,
std::tuple<T, T>,
std::tuple<T, T>>& value) {
return Math::Matrix4x2<T>{
Math::Vector2<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value))},
Math::Vector2<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value))},
Math::Vector2<T>{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value))},
Math::Vector2<T>{std::get<0>(std::get<3>(value)), std::get<1>(std::get<3>(value))}
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix4x2<T>& self, const Math::Matrix2x4<T>& other) -> Math::Matrix2x2<T> {
return self*other;
}, "Multiply a matrix")
@ -315,6 +374,17 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector3<T>, Math::Vector3<T>, Math::Vector3<T>, Math::Vector3<T>>& value) {
return Math::Matrix4x3<T>{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T, T>,
std::tuple<T, T, T>,
std::tuple<T, T, T>,
std::tuple<T, T, T>>& value) {
return Math::Matrix4x3<T>{
Math::Vector3<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value))},
Math::Vector3<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value))},
Math::Vector3<T>{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value))},
Math::Vector3<T>{std::get<0>(std::get<3>(value)), std::get<1>(std::get<3>(value)), std::get<2>(std::get<3>(value))}
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix4x3<T>& self, const Math::Matrix2x4<T>& other) -> Math::Matrix2x3<T> {
return self*other;
}, "Multiply a matrix")
@ -333,6 +403,17 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector4<T>, Math::Vector4<T>, Math::Vector4<T>, Math::Vector4<T>>& value) {
return Math::Matrix4x4<T>{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>>& value) {
return Math::Matrix4x4<T>{
Math::Vector4<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value)), std::get<3>(std::get<0>(value))},
Math::Vector4<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value)), std::get<3>(std::get<1>(value))},
Math::Vector4<T>{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value)), std::get<3>(std::get<2>(value))},
Math::Vector4<T>{std::get<0>(std::get<3>(value)), std::get<1>(std::get<3>(value)), std::get<2>(std::get<3>(value)), std::get<3>(std::get<3>(value))},
};
}), "Construct from a column tuple")
.def("__matmul__", [](const Math::Matrix4x4<T>& self, const Math::Matrix2x4<T>& other) -> Math::Matrix2x4<T> {
return self*other;
}, "Multiply a matrix")
@ -380,6 +461,15 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector3<T>, Math::Vector3<T>, Math::Vector3<T>>& value) {
return Math::Matrix3<T>{std::get<0>(value), std::get<1>(value), std::get<2>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T, T>,
std::tuple<T, T, T>,
std::tuple<T, T, T>>& value) {
return Math::Matrix3<T>{
Math::Vector3<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value))},
Math::Vector3<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value))},
Math::Vector3<T>{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value))}
};
}), "Construct from a column tuple")
/* Member functions */
.def("is_rigid_transformation", &Math::Matrix3<T>::isRigidTransformation,
@ -495,6 +585,17 @@ template<class T> void matrices(
.def(py::init([](const std::tuple<Math::Vector4<T>, Math::Vector4<T>, Math::Vector4<T>, Math::Vector4<T>>& value) {
return Math::Matrix4<T>{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)};
}), "Construct from a column vector tuple")
.def(py::init([](const std::tuple<std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>,
std::tuple<T, T, T, T>>& value) {
return Math::Matrix4<T>{
Math::Vector4<T>{std::get<0>(std::get<0>(value)), std::get<1>(std::get<0>(value)), std::get<2>(std::get<0>(value)), std::get<3>(std::get<0>(value))},
Math::Vector4<T>{std::get<0>(std::get<1>(value)), std::get<1>(std::get<1>(value)), std::get<2>(std::get<1>(value)), std::get<3>(std::get<1>(value))},
Math::Vector4<T>{std::get<0>(std::get<2>(value)), std::get<1>(std::get<2>(value)), std::get<2>(std::get<2>(value)), std::get<3>(std::get<2>(value))},
Math::Vector4<T>{std::get<0>(std::get<3>(value)), std::get<1>(std::get<3>(value)), std::get<2>(std::get<3>(value)), std::get<3>(std::get<3>(value))},
};
}), "Construct from a column tuple")
/* Member functions */
.def("is_rigid_transformation", &Math::Matrix4<T>::isRigidTransformation,

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

@ -371,12 +371,133 @@ class Matrix(unittest.TestCase):
self.assertEqual(e[0], Vector3(1.0, 2.0, 3.0))
self.assertEqual(e[1], Vector3(4.0, 5.0, 6.0))
f = Matrix3x2(((1.0, 2.0),
def test_init_tuple_of_vectors(self):
a = Matrix2x2((Vector2(1.0, 2.0),
Vector2(3.0, 4.0)))
self.assertEqual(a, Matrix2x2(Vector2(1.0, 2.0),
Vector2(3.0, 4.0)))
a = Matrix2x3((Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0)))
self.assertEqual(a, Matrix2x3(Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0)))
a = Matrix2x4((Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0)))
self.assertEqual(a, Matrix2x4(Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0)))
a = Matrix3x2((Vector2(1.0, 2.0),
Vector2(3.0, 4.0),
Vector2(5.0, 6.0)))
self.assertEqual(a, Matrix3x2(Vector2(1.0, 2.0),
Vector2(3.0, 4.0),
Vector2(5.0, 6.0)))
a = Matrix3x3((Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0)))
self.assertEqual(a, Matrix3x3(Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0)))
a = Matrix3x4((Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0)))
self.assertEqual(a, Matrix3x4(Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0)))
a = Matrix4x2((Vector2(1.0, 2.0),
Vector2(3.0, 4.0),
Vector2(5.0, 6.0),
Vector2(7.0, 8.0)))
self.assertEqual(a, Matrix4x2(Vector2(1.0, 2.0),
Vector2(3.0, 4.0),
Vector2(5.0, 6.0),
Vector2(7.0, 8.0)))
a = Matrix4x3((Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0),
Vector3(10.0, 11.0, 12.0)))
self.assertEqual(a, Matrix4x3(Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0),
Vector3(10.0, 11.0, 12.0)))
a = Matrix4x4((Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0),
Vector4(13.0, 14.0, 15.0, 16.0)))
self.assertEqual(a, Matrix4x4(Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0),
Vector4(13.0, 14.0, 15.0, 16.0)))
def test_init_tuple_of_tuples(self):
a = Matrix2x2(((1.0, 2.0),
(3.0, 4.0)))
self.assertEqual(a, Matrix2x2(Vector2(1.0, 2.0),
Vector2(3.0, 4.0)))
a = Matrix2x3(((1.0, 2.0, 3.0),
(4.0, 5.0, 6.0)))
self.assertEqual(a, Matrix2x3(Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0)))
a = Matrix2x4(((1.0, 2.0, 3.0, 4.0),
(5.0, 6.0, 7.0, 8.0)))
self.assertEqual(a, Matrix2x4(Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0)))
a = Matrix3x2(((1.0, 2.0),
(3.0, 4.0),
(5.0, 6.0)))
self.assertEqual(f[0], Vector2(1.0, 2.0))
self.assertEqual(f[1], Vector2(3.0, 4.0))
self.assertEqual(f[2], Vector2(5.0, 6.0))
self.assertEqual(a, Matrix3x2(Vector2(1.0, 2.0),
Vector2(3.0, 4.0),
Vector2(5.0, 6.0)))
a = Matrix3x3(((1.0, 2.0, 3.0),
(4.0, 5.0, 6.0),
(7.0, 8.0, 9.0)))
self.assertEqual(a, Matrix3x3(Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0)))
a = Matrix3x4(((1.0, 2.0, 3.0, 4.0),
(5.0, 6.0, 7.0, 8.0),
(9.0, 10.0, 11.0, 12.0)))
self.assertEqual(a, Matrix3x4(Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0)))
a = Matrix4x2(((1.0, 2.0),
(3.0, 4.0),
(5.0, 6.0),
(7.0, 8.0)))
self.assertEqual(a, Matrix4x2(Vector2(1.0, 2.0),
Vector2(3.0, 4.0),
Vector2(5.0, 6.0),
Vector2(7.0, 8.0)))
a = Matrix4x3(((1.0, 2.0, 3.0),
(4.0, 5.0, 6.0),
(7.0, 8.0, 9.0),
(10.0, 11.0, 12.0)))
self.assertEqual(a, Matrix4x3(Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0),
Vector3(10.0, 11.0, 12.0)))
a = Matrix4x4(((1.0, 2.0, 3.0, 4.0),
(5.0, 6.0, 7.0, 8.0),
(9.0, 10.0, 11.0, 12.0),
(13.0, 14.0, 15.0, 16.0)))
self.assertEqual(a, Matrix4x4(Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0),
Vector4(13.0, 14.0, 15.0, 16.0)))
def test_convert(self):
a = Matrix2x3d(Matrix2x3((1.0, 2.0, 3.0),
@ -462,13 +583,29 @@ class Matrix3_(unittest.TestCase):
self.assertEqual(c3[1], Vector3.y_axis(3.0))
self.assertEqual(c3[2], Vector3.z_axis(3.0))
d = Matrix3(((1.0, 2.0, 3.0),
(4.0, 5.0, 6.0),
(7.0, 8.0, 9.0)))
d = Matrix3((1.0, 2.0, 3.0),
(4.0, 5.0, 6.0),
(7.0, 8.0, 9.0))
self.assertEqual(d[0], Vector3(1.0, 2.0, 3.0))
self.assertEqual(d[1], Vector3(4.0, 5.0, 6.0))
self.assertEqual(d[2], Vector3(7.0, 8.0, 9.0))
# Tuple of vectors
e = Matrix3((Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0)))
self.assertEqual(e, Matrix3(Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0)))
# Tuple of tuples
e = Matrix3(((1.0, 2.0, 3.0),
(4.0, 5.0, 6.0),
(7.0, 8.0, 9.0)))
self.assertEqual(e, Matrix3(Vector3(1.0, 2.0, 3.0),
Vector3(4.0, 5.0, 6.0),
Vector3(7.0, 8.0, 9.0)))
def test_convert(self):
a = Matrix3(Matrix3d((1.0, 2.0, 3.0),
(4.0, 5.0, 6.0),
@ -527,15 +664,35 @@ class Matrix4_(unittest.TestCase):
self.assertEqual(c3[2], Vector4(0.0, 0.0, 3.0, 0.0))
self.assertEqual(c3[3], Vector4(0.0, 0.0, 0.0, 3.0))
d = Matrix4(((1.0, 2.0, 3.0, 4.0),
(5.0, 6.0, 7.0, 8.0),
(9.0, 10.0, 11.0, 12.0),
(13.0, 14.0, 15.0, 16.0)))
d = Matrix4((1.0, 2.0, 3.0, 4.0),
(5.0, 6.0, 7.0, 8.0),
(9.0, 10.0, 11.0, 12.0),
(13.0, 14.0, 15.0, 16.0))
self.assertEqual(d[0], Vector4(1.0, 2.0, 3.0, 4.0))
self.assertEqual(d[1], Vector4(5.0, 6.0, 7.0, 8.0))
self.assertEqual(d[2], Vector4(9.0, 10.0, 11.0, 12.0))
self.assertEqual(d[3], Vector4(13.0, 14.0, 15.0, 16.0))
# Tuple of vectors
e = Matrix4((Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0),
Vector4(13.0, 14.0, 15.0, 16.0)))
self.assertEqual(e, Matrix4(Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0),
Vector4(13.0, 14.0, 15.0, 16.0)))
# Tuple of tuples
e = Matrix4(((1.0, 2.0, 3.0, 4.0),
(5.0, 6.0, 7.0, 8.0),
(9.0, 10.0, 11.0, 12.0),
(13.0, 14.0, 15.0, 16.0)))
self.assertEqual(e, Matrix4(Vector4(1.0, 2.0, 3.0, 4.0),
Vector4(5.0, 6.0, 7.0, 8.0),
Vector4(9.0, 10.0, 11.0, 12.0),
Vector4(13.0, 14.0, 15.0, 16.0)))
def test_convert(self):
a = Matrix4d(Matrix4((1.0, 2.0, 3.0, 4.0),
(5.0, 6.0, 7.0, 8.0),

Loading…
Cancel
Save