summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoruramer <antonuramer@gmail.com>2022-01-17 22:35:06 +0000
committerPetr Mikheev <ptmikheev@gmail.com>2022-01-17 22:35:06 +0000
commitd1d8f058acf1e8494e114829a9b5e398a0a92efb (patch)
treef357058a497c48d9e9b7f260ef9e3265237eb673
parent4e9371658472f815bd2f028ad00f0d63546dd9be (diff)
Lua bindings for Colours
-rw-r--r--apps/openmw_test_suite/lua/test_serialization.cpp24
-rw-r--r--apps/openmw_test_suite/lua/test_utilpackage.cpp46
-rw-r--r--components/CMakeLists.txt2
-rw-r--r--components/lua/serialization.cpp41
-rw-r--r--components/lua/utilpackage.cpp108
-rw-r--r--components/lua/utilpackage.hpp2
-rw-r--r--components/misc/color.cpp59
-rw-r--r--components/misc/color.hpp34
-rw-r--r--files/lua_api/openmw/util.lua113
9 files changed, 390 insertions, 39 deletions
diff --git a/apps/openmw_test_suite/lua/test_serialization.cpp b/apps/openmw_test_suite/lua/test_serialization.cpp
index 1d664b06a4..5a6d5b7e51 100644
--- a/apps/openmw_test_suite/lua/test_serialization.cpp
+++ b/apps/openmw_test_suite/lua/test_serialization.cpp
@@ -5,11 +5,13 @@
#include <osg/Quat>
#include <osg/Vec2f>
#include <osg/Vec3f>
+#include <osg/Vec4f>
#include <components/lua/serialization.hpp>
#include <components/lua/utilpackage.hpp>
#include <components/misc/endianness.hpp>
+#include <components/misc/color.hpp>
#include "testing_util.hpp"
@@ -90,6 +92,7 @@ namespace
sol::state lua;
osg::Vec2f vec2(1, 2);
osg::Vec3f vec3(1, 2, 3);
+ osg::Vec4f vec4(1, 2, 3, 4);
{
std::string serialized = LuaUtil::serialize(sol::make_object(lua, vec2));
@@ -105,6 +108,27 @@ namespace
ASSERT_TRUE(value.is<osg::Vec3f>());
EXPECT_EQ(value.as<osg::Vec3f>(), vec3);
}
+ {
+ std::string serialized = LuaUtil::serialize(sol::make_object(lua, vec4));
+ EXPECT_EQ(serialized.size(), 34); // version, type, 4x double
+ sol::object value = LuaUtil::deserialize(lua, serialized);
+ ASSERT_TRUE(value.is<osg::Vec4f>());
+ EXPECT_EQ(value.as<osg::Vec4f>(), vec4);
+ }
+ }
+
+ TEST(LuaSerializationTest, Color)
+ {
+ sol::state lua;
+ Misc::Color color(1, 1, 1, 1);
+
+ {
+ std::string serialized = LuaUtil::serialize(sol::make_object(lua, color));
+ EXPECT_EQ(serialized.size(), 18); // version, type, 4x float
+ sol::object value = LuaUtil::deserialize(lua, serialized);
+ ASSERT_TRUE(value.is<Misc::Color>());
+ EXPECT_EQ(value.as<Misc::Color>(), color);
+ }
}
TEST(LuaSerializationTest, Transform) {
diff --git a/apps/openmw_test_suite/lua/test_utilpackage.cpp b/apps/openmw_test_suite/lua/test_utilpackage.cpp
index 953d5f50d3..ba19cca383 100644
--- a/apps/openmw_test_suite/lua/test_utilpackage.cpp
+++ b/apps/openmw_test_suite/lua/test_utilpackage.cpp
@@ -70,6 +70,52 @@ namespace
EXPECT_FLOAT_EQ(get<float>(lua, "len"), 0);
}
+ TEST(LuaUtilPackageTest, Vector4)
+ {
+ sol::state lua;
+ lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
+ lua["util"] = LuaUtil::initUtilPackage(lua);
+ lua.safe_script("v = util.vector4(5, 12, 13, 15)");
+ EXPECT_FLOAT_EQ(get<float>(lua, "v.x"), 5);
+ EXPECT_FLOAT_EQ(get<float>(lua, "v.y"), 12);
+ EXPECT_FLOAT_EQ(get<float>(lua, "v.z"), 13);
+ EXPECT_FLOAT_EQ(get<float>(lua, "v.w"), 15);
+ EXPECT_EQ(get<std::string>(lua, "tostring(v)"), "(5, 12, 13, 15)");
+ EXPECT_FLOAT_EQ(get<float>(lua, "util.vector4(4, 0, 0, 3):length()"), 5);
+ EXPECT_FLOAT_EQ(get<float>(lua, "util.vector4(4, 0, 0, 3):length2()"), 25);
+ EXPECT_FALSE(get<bool>(lua, "util.vector4(1, 2, 3, 4) == util.vector4(1, 3, 2, 4)"));
+ EXPECT_TRUE(get<bool>(lua, "util.vector4(1, 2, 3, 4) + util.vector4(2, 5, 1, 2) == util.vector4(3, 7, 4, 6)"));
+ EXPECT_TRUE(get<bool>(lua, "util.vector4(1, 2, 3, 4) - util.vector4(2, 5, 1, 7) == -util.vector4(1, 3, -2, 3)"));
+ EXPECT_TRUE(get<bool>(lua, "util.vector4(1, 2, 3, 4) == util.vector4(2, 4, 6, 8) / 2"));
+ EXPECT_TRUE(get<bool>(lua, "util.vector4(1, 2, 3, 4) * 2 == util.vector4(2, 4, 6, 8)"));
+ EXPECT_FLOAT_EQ(get<float>(lua, "util.vector4(3, 2, 1, 4) * v"), 5 * 3 + 12 * 2 + 13 * 1 + 15 * 4);
+ EXPECT_FLOAT_EQ(get<float>(lua, "util.vector4(3, 2, 1, 4):dot(v)"), 5 * 3 + 12 * 2 + 13 * 1 + 15 * 4);
+ lua.safe_script("v2, len = util.vector4(3, 0, 0, 4):normalize()");
+ EXPECT_FLOAT_EQ(get<float>(lua, "len"), 5);
+ EXPECT_TRUE(get<bool>(lua, "v2 == util.vector4(3/5, 0, 0, 4/5)"));
+ lua.safe_script("_, len = util.vector4(0, 0, 0, 0):normalize()");
+ EXPECT_FLOAT_EQ(get<float>(lua, "len"), 0);
+ }
+
+ TEST(LuaUtilPackageTest, Color)
+ {
+ sol::state lua;
+ lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string);
+ lua["util"] = LuaUtil::initUtilPackage(lua);
+ lua.safe_script("brown = util.color.rgba(0.75, 0.25, 0, 1)");
+ EXPECT_EQ(get<std::string>(lua, "tostring(brown)"), "(0.75, 0.25, 0, 1)");
+ lua.safe_script("blue = util.color.rgb(0, 1, 0, 1)");
+ EXPECT_EQ(get<std::string>(lua, "tostring(blue)"), "(0, 1, 0, 1)");
+ lua.safe_script("red = util.color.hex('ff0000')");
+ EXPECT_EQ(get<std::string>(lua, "tostring(red)"), "(1, 0, 0, 1)");
+ lua.safe_script("green = util.color.hex('00FF00')");
+ EXPECT_EQ(get<std::string>(lua, "tostring(green)"), "(0, 1, 0, 1)");
+ lua.safe_script("darkRed = util.color.hex('a01112')");
+ EXPECT_EQ(get<std::string>(lua, "darkRed:asHex()"), "a01112");
+ EXPECT_TRUE(get<bool>(lua, "green:asRgba() == util.vector4(0, 1, 0, 1)"));
+ EXPECT_TRUE(get<bool>(lua, "red:asRgb() == util.vector3(1, 0, 0)"));
+ }
+
TEST(LuaUtilPackageTest, Transform)
{
sol::state lua;
diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt
index f7382db1c7..59f1e331e4 100644
--- a/components/CMakeLists.txt
+++ b/components/CMakeLists.txt
@@ -93,7 +93,7 @@ add_component_dir (esmterrain
add_component_dir (misc
constants utf8stream stringops resourcehelpers rng messageformatparser weakcache thread
- compression osguservalues errorMarker
+ compression osguservalues errorMarker color
)
add_component_dir (debug
diff --git a/components/lua/serialization.cpp b/components/lua/serialization.cpp
index e8f66c698c..d0bbbc9dc5 100644
--- a/components/lua/serialization.cpp
+++ b/components/lua/serialization.cpp
@@ -6,6 +6,7 @@
#include <osg/Vec3f>
#include <osg/Vec4f>
+#include <components/misc/color.hpp>
#include <components/misc/endianness.hpp>
#include "luastate.hpp"
@@ -28,6 +29,8 @@ namespace LuaUtil
VEC3 = 0x11,
TRANSFORM_M = 0x12,
TRANSFORM_Q = 0x13,
+ VEC4 = 0x14,
+ COLOR = 0x15,
// All values should be lesser than 0x20 (SHORT_STRING_FLAG).
};
@@ -129,6 +132,26 @@ namespace LuaUtil
appendValue<double>(out, quat[i]);
return;
}
+ if (data.is<osg::Vec4f>())
+ {
+ appendType(out, SerializedType::VEC4);
+ osg::Vec4f v = data.as<osg::Vec4f>();
+ appendValue<double>(out, v.x());
+ appendValue<double>(out, v.y());
+ appendValue<double>(out, v.z());
+ appendValue<double>(out, v.w());
+ return;
+ }
+ if (data.is<Misc::Color>())
+ {
+ appendType(out, SerializedType::COLOR);
+ Misc::Color v = data.as<Misc::Color> ();
+ appendValue<float>(out, v.r());
+ appendValue<float>(out, v.g());
+ appendValue<float>(out, v.b());
+ appendValue<float>(out, v.a());
+ return;
+ }
if (customSerializer && customSerializer->serialize(out, data))
return;
else
@@ -271,6 +294,24 @@ namespace LuaUtil
sol::stack::push<TransformQ>(lua, asTransform(q));
return;
}
+ case SerializedType::VEC4:
+ {
+ float x = getValue<double>(binaryData);
+ float y = getValue<double>(binaryData);
+ float z = getValue<double>(binaryData);
+ float w = getValue<double>(binaryData);
+ sol::stack::push<osg::Vec4f>(lua, osg::Vec4f(x, y, z, w));
+ return;
+ }
+ case SerializedType::COLOR:
+ {
+ float r = getValue<float>(binaryData);
+ float g = getValue<float>(binaryData);
+ float b = getValue<float>(binaryData);
+ float a = getValue<float>(binaryData);
+ sol::stack::push<Misc::Color>(lua, Misc::Color(r, g, b, a));
+ return;
+ }
}
throw std::runtime_error("Unknown type in serialized data: " + std::to_string(type));
}
diff --git a/components/lua/utilpackage.cpp b/components/lua/utilpackage.cpp
index abb680a6bc..c51b00a7d5 100644
--- a/components/lua/utilpackage.cpp
+++ b/components/lua/utilpackage.cpp
@@ -2,8 +2,10 @@
#include <algorithm>
#include <sstream>
+#include <array>
#include <components/misc/mathutil.hpp>
+#include <components/misc/color.hpp>
#include "luastate.hpp"
@@ -16,6 +18,12 @@ namespace sol
struct is_automagical<LuaUtil::Vec3> : std::false_type {};
template <>
+ struct is_automagical<LuaUtil::Vec4> : std::false_type {};
+
+ template <>
+ struct is_automagical<Misc::Color> : std::false_type {};
+
+ template <>
struct is_automagical<LuaUtil::TransformM> : std::false_type {};
template <>
@@ -24,6 +32,31 @@ namespace sol
namespace LuaUtil
{
+ namespace {
+ template<typename T>
+ void addVectorMethods(sol::usertype<T>& vectorType)
+ {
+ vectorType[sol::meta_function::unary_minus] = [](const T& a) { return -a; };
+ vectorType[sol::meta_function::addition] = [](const T& a, const T& b) { return a + b; };
+ vectorType[sol::meta_function::subtraction] = [](const T& a, const T& b) { return a - b; };
+ vectorType[sol::meta_function::equal_to] = [](const T& a, const T& b) { return a == b; };
+ vectorType[sol::meta_function::multiplication] = sol::overload(
+ [](const T& a, float c) { return a * c; },
+ [](const T& a, const T& b) { return a * b; });
+ vectorType[sol::meta_function::division] = [](const T& a, float c) { return a / c; };
+ vectorType["dot"] = [](const T& a, const T b) { return a * b; };
+ vectorType["length"] = &T::length;
+ vectorType["length2"] = &T::length2;
+ vectorType["normalize"] = [](const T& v)
+ {
+ float len = v.length();
+ if (len == 0)
+ return std::make_tuple(T(), 0.f);
+ else
+ return std::make_tuple(v * (1.f / len), len);
+ };
+ }
+ }
sol::table initUtilPackage(sol::state& lua)
{
@@ -34,29 +67,13 @@ namespace LuaUtil
sol::usertype<Vec2> vec2Type = lua.new_usertype<Vec2>("Vec2");
vec2Type["x"] = sol::readonly_property([](const Vec2& v) -> float { return v.x(); } );
vec2Type["y"] = sol::readonly_property([](const Vec2& v) -> float { return v.y(); } );
- vec2Type[sol::meta_function::to_string] = [](const Vec2& v) {
+ vec2Type[sol::meta_function::to_string] = [](const Vec2& v)
+ {
std::stringstream ss;
ss << "(" << v.x() << ", " << v.y() << ")";
return ss.str();
};
- vec2Type[sol::meta_function::unary_minus] = [](const Vec2& a) { return -a; };
- vec2Type[sol::meta_function::addition] = [](const Vec2& a, const Vec2& b) { return a + b; };
- vec2Type[sol::meta_function::subtraction] = [](const Vec2& a, const Vec2& b) { return a - b; };
- vec2Type[sol::meta_function::equal_to] = [](const Vec2& a, const Vec2& b) { return a == b; };
- vec2Type[sol::meta_function::multiplication] = sol::overload(
- [](const Vec2& a, float c) { return a * c; },
- [](const Vec2& a, const Vec2& b) { return a * b; });
- vec2Type[sol::meta_function::division] = [](const Vec2& a, float c) { return a / c; };
- vec2Type["dot"] = [](const Vec2& a, const Vec2& b) { return a * b; };
- vec2Type["length"] = &Vec2::length;
- vec2Type["length2"] = &Vec2::length2;
- vec2Type["normalize"] = [](const Vec2& v) {
- float len = v.length();
- if (len == 0)
- return std::make_tuple(Vec2(), 0.f);
- else
- return std::make_tuple(v * (1.f / len), len);
- };
+ addVectorMethods<Vec2>(vec2Type);
vec2Type["rotate"] = &Misc::rotateVec2f;
// Lua bindings for Vec3
@@ -65,31 +82,48 @@ namespace LuaUtil
vec3Type["x"] = sol::readonly_property([](const Vec3& v) -> float { return v.x(); } );
vec3Type["y"] = sol::readonly_property([](const Vec3& v) -> float { return v.y(); } );
vec3Type["z"] = sol::readonly_property([](const Vec3& v) -> float { return v.z(); } );
- vec3Type[sol::meta_function::to_string] = [](const Vec3& v) {
+ vec3Type[sol::meta_function::to_string] = [](const Vec3& v)
+ {
std::stringstream ss;
ss << "(" << v.x() << ", " << v.y() << ", " << v.z() << ")";
return ss.str();
};
- vec3Type[sol::meta_function::unary_minus] = [](const Vec3& a) { return -a; };
- vec3Type[sol::meta_function::addition] = [](const Vec3& a, const Vec3& b) { return a + b; };
- vec3Type[sol::meta_function::subtraction] = [](const Vec3& a, const Vec3& b) { return a - b; };
- vec3Type[sol::meta_function::equal_to] = [](const Vec3& a, const Vec3& b) { return a == b; };
- vec3Type[sol::meta_function::multiplication] = sol::overload(
- [](const Vec3& a, float c) { return a * c; },
- [](const Vec3& a, const Vec3& b) { return a * b; });
- vec3Type[sol::meta_function::division] = [](const Vec3& a, float c) { return a / c; };
+ addVectorMethods<Vec3>(vec3Type);
vec3Type[sol::meta_function::involution] = [](const Vec3& a, const Vec3& b) { return a ^ b; };
- vec3Type["dot"] = [](const Vec3& a, const Vec3& b) { return a * b; };
vec3Type["cross"] = [](const Vec3& a, const Vec3& b) { return a ^ b; };
- vec3Type["length"] = &Vec3::length;
- vec3Type["length2"] = &Vec3::length2;
- vec3Type["normalize"] = [](const Vec3& v) {
- float len = v.length();
- if (len == 0)
- return std::make_tuple(Vec3(), 0.f);
- else
- return std::make_tuple(v * (1.f / len), len);
+
+ // Lua bindings for Vec4
+ util["vector4"] = [](float x, float y, float z, float w)
+ { return Vec4(x, y, z, w); };
+ sol::usertype<Vec4> vec4Type = lua.new_usertype<Vec4>("Vec4");
+ vec4Type["x"] = sol::readonly_property([](const Vec4& v) -> float { return v.x(); });
+ vec4Type["y"] = sol::readonly_property([](const Vec4& v) -> float { return v.y(); });
+ vec4Type["z"] = sol::readonly_property([](const Vec4& v) -> float { return v.z(); });
+ vec4Type["w"] = sol::readonly_property([](const Vec4& v) -> float { return v.w(); });
+ vec4Type[sol::meta_function::to_string] = [](const Vec4& v)
+ {
+ std::stringstream ss;
+ ss << "(" << v.x() << ", " << v.y() << ", " << v.z() << ", " << v.w() << ")";
+ return ss.str();
};
+ addVectorMethods<Vec4>(vec4Type);
+
+ // Lua bindings for Color
+ sol::usertype<Misc::Color> colorType = lua.new_usertype<Misc::Color>("Color");
+ colorType["r"] = [](const Misc::Color& c) { return c.r(); };
+ colorType["g"] = [](const Misc::Color& c) { return c.g(); };
+ colorType["b"] = [](const Misc::Color& c) { return c.b(); };
+ colorType["a"] = [](const Misc::Color& c) { return c.a(); };
+ colorType[sol::meta_function::to_string] = [](const Misc::Color& c) { return c.toString(); };
+ colorType["asRgba"] = [](const Misc::Color& c) { return Vec4(c.r(), c.g(), c.b(), c.a()); };
+ colorType["asRgb"] = [](const Misc::Color& c) { return Vec3(c.r(), c.g(), c.b()); };
+ colorType["asHex"] = [](const Misc::Color& c) { return c.toHex(); };
+
+ sol::table color(lua, sol::create);
+ color["rgba"] = [](float r, float g, float b, float a) { return Misc::Color(r, g, b, a); };
+ color["rgb"] = [](float r, float g, float b) { return Misc::Color(r, g, b, 1); };
+ color["hex"] = [](std::string_view hex) { return Misc::Color::fromHex(hex); };
+ util["color"] = LuaUtil::makeReadOnly(color);
// Lua bindings for Transform
sol::usertype<TransformM> transMType = lua.new_usertype<TransformM>("TransformM");
diff --git a/components/lua/utilpackage.hpp b/components/lua/utilpackage.hpp
index d26bfdb027..a647b682af 100644
--- a/components/lua/utilpackage.hpp
+++ b/components/lua/utilpackage.hpp
@@ -3,6 +3,7 @@
#include <osg/Vec2>
#include <osg/Vec3>
+#include <osg/Vec4>
#include <osg/Matrix>
#include <sol/sol.hpp>
@@ -11,6 +12,7 @@ namespace LuaUtil
{
using Vec2 = osg::Vec2f;
using Vec3 = osg::Vec3f;
+ using Vec4 = osg::Vec4f;
// For performance reasons "Transform" is implemented as 2 types with the same interface.
// Transform supports only composition, inversion, and applying to a 3d vector.
diff --git a/components/misc/color.cpp b/components/misc/color.cpp
new file mode 100644
index 0000000000..edf3435428
--- /dev/null
+++ b/components/misc/color.cpp
@@ -0,0 +1,59 @@
+#include "color.hpp"
+
+#include <charconv>
+#include <array>
+#include <sstream>
+#include <algorithm>
+
+namespace Misc
+{
+ Color::Color(float r, float g, float b, float a)
+ : mR(std::clamp(r, 0.f, 1.f))
+ , mG(std::clamp(g, 0.f, 1.f))
+ , mB(std::clamp(b, 0.f, 1.f))
+ , mA(std::clamp(a, 0.f, 1.f))
+ {}
+
+ std::string Color::toString() const
+ {
+ std::ostringstream ss;
+ ss << "(" << r() << ", " << g() << ", " << b() << ", " << a() << ')';
+ return ss.str();
+ }
+
+ Color Color::fromHex(std::string_view hex)
+ {
+ if (hex.size() != 6)
+ throw std::logic_error(std::string("Invalid hex color: ") += hex);
+ std::array<float, 3> rgb;
+ for (size_t i = 0; i < rgb.size(); i++)
+ {
+ auto sub = hex.substr(i * 2, 2);
+ int v;
+ auto [_, ec] = std::from_chars(sub.data(), sub.data() + sub.size(), v, 16);
+ if (ec != std::errc())
+ throw std::logic_error(std::string("Invalid hex color: ") += hex);
+ rgb[i] = v / 255.0f;
+ }
+ return Color(rgb[0], rgb[1], rgb[2], 1);
+ }
+
+ std::string Color::toHex() const
+ {
+ std::string result(6, '0');
+ std::array<float, 3> rgb = { mR, mG, mB };
+ for (size_t i = 0; i < rgb.size(); i++)
+ {
+ int b = static_cast<int>(rgb[i] * 255.0f);
+ auto [_, ec] = std::to_chars(result.data() + i * 2, result.data() + (i + 1) * 2, b, 16);
+ if (ec != std::errc())
+ throw std::logic_error("Error when converting number to base 16");
+ }
+ return result;
+ }
+
+ bool operator==(const Color& l, const Color& r)
+ {
+ return l.mR == r.mR && l.mG == r.mG && l.mB == r.mB && l.mA == r.mA;
+ }
+}
diff --git a/components/misc/color.hpp b/components/misc/color.hpp
new file mode 100644
index 0000000000..932d261fad
--- /dev/null
+++ b/components/misc/color.hpp
@@ -0,0 +1,34 @@
+#ifndef COMPONENTS_MISC_COLOR
+#define COMPONENTS_MISC_COLOR
+
+#include <string>
+
+namespace Misc
+{
+ class Color
+ {
+ public:
+ Color(float r, float g, float b, float a);
+
+ float r() const { return mR; }
+ float g() const { return mG; }
+ float b() const { return mB; }
+ float a() const { return mA; }
+
+ std::string toString() const;
+
+ static Color fromHex(std::string_view hex);
+
+ std::string toHex() const;
+
+ friend bool operator==(const Color& l, const Color& r);
+
+ private:
+ float mR;
+ float mG;
+ float mB;
+ float mA;
+ };
+}
+
+#endif // !COMPONENTS_MISC_COLOR
diff --git a/files/lua_api/openmw/util.lua b/files/lua_api/openmw/util.lua
index 22986f09a6..ea7d037daa 100644
--- a/files/lua_api/openmw/util.lua
+++ b/files/lua_api/openmw/util.lua
@@ -133,7 +133,7 @@
-------------------------------------------------------------------------------
-- Normalizes vector.
-- Returns two values: normalized vector and the length of the original vector.
--- It doesn't change the original vector.
+-- It doesn't change the original vector.
-- @function [parent=#Vector3] normalize
-- @param self
-- @return #Vector3, #number
@@ -152,6 +152,117 @@
-- @param #Vector3 v
-- @return #Vector3
+
+-------------------------------------------------------------------------------
+-- Immutable 4D vector.
+-- @type Vector4
+-- @field #number x
+-- @field #number y
+-- @field #number z
+-- @field #number w
+-- @usage
+-- v = util.vector4(3, 4, 5, 6)
+-- v.x, v.y, v.z, v.w -- 3.0, 4.0, 5.0, 6.0
+-- str(v) -- "(3.0, 4.0, 5.0, 6.0)"
+-- v:length() -- length
+-- v:length2() -- square of the length
+-- v:normalize() -- normalized vector
+-- v1:dot(v2) -- dot product (returns a number)
+-- v1 * v2 -- dot product (returns a number)
+-- v1 + v2 -- vector addition
+-- v1 - v2 -- vector subtraction
+-- v1 * x -- multiplication by a number
+-- v1 / x -- division by a number
+
+-------------------------------------------------------------------------------
+-- Creates a new 4D vector. Vectors are immutable and can not be changed after creation.
+-- @function [parent=#util] vector4
+-- @param #number x.
+-- @param #number y.
+-- @param #number z.
+-- @param #number w.
+-- @return #Vector4.
+
+-------------------------------------------------------------------------------
+-- Length of the vector
+-- @function [parent=#Vector4] length
+-- @param self
+-- @return #number
+
+-------------------------------------------------------------------------------
+-- Square of the length of the vector
+-- @function [parent=#Vector4] length2
+-- @param self
+-- @return #number
+
+-------------------------------------------------------------------------------
+-- Normalizes vector.
+-- Returns two values: normalized vector and the length of the original vector.
+-- It doesn't change the original vector.
+-- @function [parent=#Vector4] normalize
+-- @param self
+-- @return #Vector4, #number
+
+-------------------------------------------------------------------------------
+-- Dot product.
+-- @function [parent=#Vector4] dot
+-- @param self
+-- @param #Vector4 v
+-- @return #number
+
+-------------------------------------------------------------------------------
+-- Color in RGBA format. All of the component values are in the range [0, 1].
+-- @type Color
+-- @field #number r Red component
+-- @field #number g Green component
+-- @field #number b Blue component
+-- @field #number a Alpha (transparency) component
+
+-------------------------------------------------------------------------------
+-- Returns a Vector4 with RGBA components of the Color.
+-- @function [parent=#Color] asRgba
+-- @param self
+-- @return #Vector4
+
+-------------------------------------------------------------------------------
+-- Returns a Vector3 with RGB components of the Color.
+-- @function [parent=#Color] asRgb
+-- @param self
+-- @return #Vector3
+
+-------------------------------------------------------------------------------
+-- Converts the color into a HEX string.
+-- @function [parent=#Color] asHex
+-- @param self
+-- @return #string
+
+-------------------------------------------------------------------------------
+-- @type COLOR
+-- @field [parent=#util] #COLOR color Methods for creating #Color values from different formats.
+
+-------------------------------------------------------------------------------
+-- Creates a Color from RGBA format
+-- @function [parent=#COLOR] rgba
+-- @param #number r
+-- @param #number g
+-- @param #number b
+-- @param #number a
+-- @return #Color
+
+-------------------------------------------------------------------------------
+-- Creates a Color from RGB format. Equivalent to calling util.rgba with a = 1.
+-- @function [parent=#COLOR] rgb
+-- @param #number r
+-- @param #number g
+-- @param #number b
+-- @return #Color
+
+-------------------------------------------------------------------------------
+-- Parses a hex color string into a Color.
+-- @function [parent=#COLOR] hex
+-- @param #string hex A hex color string in RRGGBB format (e. g. "ff0000").
+-- @return #Color
+
-------------------------------------------------------------------------------
-- @type Transform