diff --git a/src/python/magnum/__init__.py b/src/python/magnum/__init__.py index 70a3671..2b4fc7b 100644 --- a/src/python/magnum/__init__.py +++ b/src/python/magnum/__init__.py @@ -41,6 +41,7 @@ __all__ = [ 'Vector2d', 'Vector3d', 'Vector4d', 'Vector2i', 'Vector3i', 'Vector4i', 'Vector2ui', 'Vector3ui', 'Vector4ui', + 'Color3', 'Color4', 'Matrix2x2', 'Matrix2x3', 'Matrix2x4', 'Matrix3x2', 'Matrix3x3', 'Matrix3x4', diff --git a/src/python/magnum/math.vector.h b/src/python/magnum/math.vector.h index 94c9407..38efe12 100644 --- a/src/python/magnum/math.vector.h +++ b/src/python/magnum/math.vector.h @@ -26,6 +26,7 @@ */ #include +#include #include #include "magnum/math.h" @@ -260,6 +261,76 @@ template void vector4(py::class_>& c) { "XY part of the vector"); } +template void color(py::class_& c) { + c + .def_static("zero_init", []() { + return T{Math::ZeroInit}; + }, "Construct a zero color") + .def(py::init(), "Default constructor"); +} + +template void color3(py::class_, Math::Vector3>& c) { + py::implicitly_convertible&, Math::Color3>(); + + c + /* Constructors */ + .def(py::init(), "Constructor") + .def(py::init(), "Construct with one value for all components") + .def(py::init>(), "Construct from a vector") + .def(py::init([](const std::tuple& value) { + return Math::Color3{std::get<0>(value), std::get<1>(value), std::get<2>(value)}; + }), "Construct from a tuple") + + .def_static("from_hsv", [](Degd hue, typename Math::Color3::FloatingPointType saturation, typename Math::Color3::FloatingPointType value) { + return Math::Color3::fromHsv({Math::Deg(hue), saturation, value}); + }, "Create RGB color from HSV representation", py::arg("hue"), py::arg("saturation"), py::arg("value")) + + /* Accessors */ + .def("to_hsv", [](Math::Color3& self) { + auto hsv = self.toHsv(); + return std::make_tuple(Degd(hsv.hue), hsv.saturation, hsv.value); + }, "Convert to HSV representation") + .def("hue", [](Math::Color3& self) { + return Degd(self.hue()); + }, "Hue") + .def("saturation", &Math::Color3::saturation, "Saturation") + .def("value", &Math::Color3::value, "Value"); +} + +template void color4(py::class_, Math::Vector4>& c) { + py::implicitly_convertible&, Math::Color4>(); + py::implicitly_convertible&, Math::Color4>(); + py::implicitly_convertible&, Math::Color4>(); + + c + /* Constructors */ + .def(py::init(), "Constructor", py::arg("r"), py::arg("g"), py::arg("b"), py::arg("a") = Math::Implementation::fullChannel()) + .def(py::init(), "Construct with one value for all components", py::arg("rgb"), py::arg("alpha") = Math::Implementation::fullChannel()) + .def(py::init, T>(), "Construct from a vector", py::arg("rgb"), py::arg("alpha") = Math::Implementation::fullChannel()) + .def(py::init>(), "Construct from a vector") + .def(py::init([](const std::tuple& value) { + return Math::Color4{std::get<0>(value), std::get<1>(value), std::get<2>(value)}; + }), "Construct from a RGB tuple") + .def(py::init([](const std::tuple& value) { + return Math::Color4{std::get<0>(value), std::get<1>(value), std::get<2>(value), std::get<3>(value)}; + }), "Construct from a RGBA tuple") + + .def_static("from_hsv", [](Degd hue, typename Math::Color4::FloatingPointType saturation, typename Math::Color4::FloatingPointType value, T alpha) { + return Math::Color4::fromHsv({Math::Deg(hue), saturation, value}, alpha); + }, "Create RGB color from HSV representation", py::arg("hue"), py::arg("saturation"), py::arg("value"), py::arg("alpha") = Math::Implementation::fullChannel()) + + /* Accessors */ + .def("to_hsv", [](Math::Color4& self) { + auto hsv = self.toHsv(); + return std::make_tuple(Degd(hsv.hue), hsv.saturation, hsv.value); + }, "Convert to HSV representation") + .def("hue", [](Math::Color4& self) { + return Degd(self.hue()); + }, "Hue") + .def("saturation", &Math::Color4::saturation, "Saturation") + .def("value", &Math::Color4::value, "Value"); +} + } #endif diff --git a/src/python/magnum/math.vectorfloat.cpp b/src/python/magnum/math.vectorfloat.cpp index f0ef2f2..53b8c7a 100644 --- a/src/python/magnum/math.vectorfloat.cpp +++ b/src/python/magnum/math.vectorfloat.cpp @@ -85,6 +85,14 @@ void mathVectorFloat(py::module& root, py::module& m) { py::class_ vector4d{root, "Vector4d", "Four-component double vector"}; vectorsFloat(m, vector2, vector3, vector4); vectorsFloat(m, vector2d, vector3d, vector4d); + + py::class_ color3_{root, "Color3", "Color in linear RGB color space"}; + color(color3_); + color3(color3_); + + py::class_ color4_{root, "Color4", "Color in linear RGBA color space"}; + color(color4_); + color4(color4_); } } diff --git a/src/python/magnum/test/test_math.py b/src/python/magnum/test/test_math.py index e359da2..3d2947d 100644 --- a/src/python/magnum/test/test_math.py +++ b/src/python/magnum/test/test_math.py @@ -259,6 +259,81 @@ class Vector(unittest.TestCase): def test_repr(self): self.assertEqual(repr(Vector3(1.0, 3.14, -13.37)), 'Vector(1, 3.14, -13.37)') +class Color3_(unittest.TestCase): + def test_init(self): + a1 = Color3() + a2 = Color3.zero_init() + self.assertEqual(a1, Color3(0.0, 0.0, 0.0)) + self.assertEqual(a2, Color3(0.0, 0.0, 0.0)) + + b = Color3(0.5) + self.assertEqual(b, Color3(0.5, 0.5, 0.5)) + + c1 = Color3(0.5, 0.75, 1.0) + c2 = Color3((0.5, 0.75, 1.0)) + c3 = Color3(Vector3(0.5, 0.75, 1.0)) + self.assertEqual(c1, Color3(0.5, 0.75, 1.0)) + self.assertEqual(c2, Color3(0.5, 0.75, 1.0)) + self.assertEqual(c3, Color3(0.5, 0.75, 1.0)) + + def test_hsv(self): + a = Color3.from_hsv(Deg(230.0), 0.749, 0.427) + self.assertEqual(a, Color3(0.107177, 0.160481, 0.427)) + + self.assertEqual(a.hue(), Deg(230.0)) + self.assertAlmostEqual(a.saturation(), 0.749) + self.assertAlmostEqual(a.value(), 0.427) + + self.assertEqual(a.to_hsv()[0], Deg(230.0)) + self.assertAlmostEqual(a.to_hsv()[1], 0.749) + self.assertAlmostEqual(a.to_hsv()[2], 0.427) + +class Color4_(unittest.TestCase): + def test_init(self): + a1 = Color4() + a2 = Color4.zero_init() + self.assertEqual(a1, Color4(0.0, 0.0, 0.0, 0.0)) + self.assertEqual(a2, Color4(0.0, 0.0, 0.0, 0.0)) + + b = Color4(0.5) + self.assertEqual(b, Color4(0.5, 0.5, 0.5, 1.0)) + + c = Color4(0.5, 0.75) + self.assertEqual(c, Color4(0.5, 0.5, 0.5, 0.75)) + + d1 = Color4(0.5, 0.75, 0.875) + d2 = Color4((0.5, 0.75, 0.875)) + d3 = Color4(Vector3(0.5, 0.75, 0.875)) + self.assertEqual(d1, Color4(0.5, 0.75, 0.875, 1.0)) + self.assertEqual(d2, Color4(0.5, 0.75, 0.875, 1.0)) + self.assertEqual(d3, Color4(0.5, 0.75, 0.875, 1.0)) + + e1 = Color4(0.5, 0.75, 0.875, 0.9) + e2 = Color4((0.5, 0.75, 0.875), 0.9) + e3 = Color4((0.5, 0.75, 0.875, 0.9)) + e4 = Color4(Vector3(0.5, 0.75, 0.875), 0.9) + e5 = Color4(Vector4(0.5, 0.75, 0.875, 0.9)) + self.assertEqual(e1, Color4(0.5, 0.75, 0.875, 0.9)) + self.assertEqual(e2, Color4(0.5, 0.75, 0.875, 0.9)) + self.assertEqual(e3, Color4(0.5, 0.75, 0.875, 0.9)) + self.assertEqual(e4, Color4(0.5, 0.75, 0.875, 0.9)) + self.assertEqual(e5, Color4(0.5, 0.75, 0.875, 0.9)) + + def test_hsv(self): + a = Color4.from_hsv(Deg(230.0), 0.749, 0.427, 0.95) + self.assertEqual(a, Color4(0.107177, 0.160481, 0.427, 0.95)) + + self.assertEqual(a.hue(), Deg(230.0)) + self.assertAlmostEqual(a.saturation(), 0.749) + self.assertAlmostEqual(a.value(), 0.427) + + self.assertEqual(a.to_hsv()[0], Deg(230.0)) + self.assertAlmostEqual(a.to_hsv()[1], 0.749) + self.assertAlmostEqual(a.to_hsv()[2], 0.427) + + b = Color4.from_hsv(Deg(230.0), 0.749, 0.427) + self.assertEqual(b, Color4(0.107177, 0.160481, 0.427, 1.0)) + class Matrix(unittest.TestCase): def test_init(self): a = Matrix3x2()