diff --git a/Doxyfile b/Doxyfile index 23f6dc968..40f5bcd66 100644 --- a/Doxyfile +++ b/Doxyfile @@ -198,7 +198,7 @@ ALIASES = \ "debugoperator{1}=@relates \1\n@brief Debug output operator" \ "collisionoperator{2}=@relates \1\n@brief Collision of %\1 and %\2\n@see \2::operator%(const \1&) const" \ "todoc=@xrefitem todoc \"Documentation todo\" \"Documentation-related todo list\"" \ - "requires_gl=@xrefitem requires-gl \"Requires desktop OpenGL\" \"Functionality requiring desktop OpenGL (not available on OpenGL ES 2)\" Not available on OpenGL ES 2." \ + "requires_gl=@xrefitem requires-gl \"Requires desktop OpenGL\" \"Functionality requiring desktop OpenGL (not available on OpenGL ES)\" Not available on OpenGL ES." \ "requires_gl30=@xrefitem requires-gl30 \"Requires OpenGL 3.0\" \"Functionality requiring OpenGL 3.0\"" \ "requires_gl31=@xrefitem requires-gl31 \"Requires OpenGL 3.1\" \"Functionality requiring OpenGL 3.1\"" \ "requires_gl32=@xrefitem requires-gl32 \"Requires OpenGL 3.2\" \"Functionality requiring OpenGL 3.2\"" \ diff --git a/doc/mainpage.dox b/doc/mainpage.dox index 460ce6399..3e369b994 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -26,7 +26,7 @@ Features: The engine is meant to be run on OpenGL 3 capable hardware, but most of the functionality is working on OpenGL 2.1 hardware too. The engine can be built -also for OpenGL ES 2 with limited functionality. See also @ref required-extensions. +also for OpenGL ES with limited functionality. See also @ref required-extensions. @section download-build Downloading and building Magnum diff --git a/doc/required-extensions.dox b/doc/required-extensions.dox index 1b2daf898..c35629ca5 100644 --- a/doc/required-extensions.dox +++ b/doc/required-extensions.dox @@ -3,8 +3,8 @@ The engine is meant to be run on OpenGL 3 capable hardware, but most of the functionality is working in OpenGL 2.1 hardware too (i.e. integrated Intel -GPUs), unless stated otherwise. OpenGL ES 2 is also supported, see -@ref building for guide how to build the engine for it. +GPUs), unless stated otherwise. OpenGL ES is also supported, see @ref building +for guide how to build the engine for it. Following are lists of functionality requiring specific OpenGL version. In most cases it is also specified which extension is required for given @@ -22,7 +22,7 @@ supported on Intel GPUs even if they are capable of OpenGL 2.1 only). - @subpage requires-gl42 - @subpage requires-extension -@page requires-gl Functionality requiring desktop OpenGL (not available on OpenGL ES 2) +@page requires-gl Functionality requiring desktop OpenGL (not available on OpenGL ES) @page requires-gl30 Functionality requiring OpenGL 3.0 @page requires-gl31 Functionality requiring OpenGL 3.1 @page requires-gl32 Functionality requiring OpenGL 3.2 diff --git a/src/AbstractImage.h b/src/AbstractImage.h index a8d9f0c5c..3dea087db 100644 --- a/src/AbstractImage.h +++ b/src/AbstractImage.h @@ -72,9 +72,11 @@ class MAGNUM_EXPORT AbstractImage { #endif RGB = GL_RGB, /**< Three-component RGB */ - RGBA = GL_RGBA, /**< Four-component RGBA */ + RGBA = GL_RGBA /**< Four-component RGBA */ #ifndef MAGNUM_TARGET_GLES + , + /** * Three-component BGR * @requires_gl @@ -86,16 +88,19 @@ class MAGNUM_EXPORT AbstractImage { * @requires_gl */ BGRA = GL_BGRA, - #endif - /** Depth component. For framebuffer reading only. */ + /** + * Depth component. For framebuffer reading only. + * @requires_gl + */ Depth = GL_DEPTH_COMPONENT, - /** Stencil index. For framebuffer reading only. */ - StencilIndex = GL_STENCIL_INDEX + /** + * Stencil index. For framebuffer reading only. + * @requires_gl + */ + StencilIndex = GL_STENCIL_INDEX, - #ifndef MAGNUM_TARGET_GLES - , /** * Depth and stencil component. For framebuffer reading only. * @requires_gl diff --git a/src/AbstractShaderProgram.cpp b/src/AbstractShaderProgram.cpp index e8fd69658..4fd606973 100644 --- a/src/AbstractShaderProgram.cpp +++ b/src/AbstractShaderProgram.cpp @@ -39,14 +39,14 @@ bool AbstractShaderProgram::attachShader(Shader& shader) { } void AbstractShaderProgram::bindAttributeLocation(GLuint location, const string& name) { - CORRADE_ASSERT(state == Initialized, "AbstractShaderProgram: attribute cannot be bound after linking.", ) + CORRADE_ASSERT(state == Initialized, "AbstractShaderProgram: attribute cannot be bound after linking.", ); glBindAttribLocation(program, location, name.c_str()); } #ifndef MAGNUM_TARGET_GLES void AbstractShaderProgram::bindFragmentDataLocation(GLuint location, const std::string& name) { - CORRADE_ASSERT(state == Initialized, "AbstractShaderProgram: fragment data location cannot be bound after linking.", ) + CORRADE_ASSERT(state == Initialized, "AbstractShaderProgram: fragment data location cannot be bound after linking.", ); glBindFragDataLocation(program, location, name.c_str()); } @@ -83,7 +83,7 @@ void AbstractShaderProgram::link() { GLint AbstractShaderProgram::uniformLocation(const std::string& name) { /** @todo What if linking just failed (not programmer error?) */ - CORRADE_ASSERT(state == Linked, "AbstractShaderProgram: uniform location cannot be retrieved before linking.", -1) + CORRADE_ASSERT(state == Linked, "AbstractShaderProgram: uniform location cannot be retrieved before linking.", -1); GLint location = glGetUniformLocation(program, name.c_str()); if(location == -1) diff --git a/src/AbstractShaderProgram.h b/src/AbstractShaderProgram.h index 5027984ca..d15c2aeb9 100644 --- a/src/AbstractShaderProgram.h +++ b/src/AbstractShaderProgram.h @@ -28,7 +28,7 @@ namespace Magnum { /** @ingroup rendering @brief Base class for shaders -@section AbstractShaderProgramSubclassing Subclassing workflow +@section AbstractShaderProgram-subclassing Subclassing workflow This class is designed to be used via subclassing. Subclasses define these functions and properties: @@ -73,7 +73,7 @@ void setProjectionMatrixUniform(const Matrix4& matrix) { } @endcode -@subsection AbstractShaderProgramAttributeLocation Binding attribute location +@subsection AbstractShaderProgram-attribute-location Binding attribute location The preferred workflow is to specify attribute location for vertex shader input attributes and fragment shader output attributes explicitly in the shader code, e.g.: @@ -82,8 +82,12 @@ layout(location = 0) in vec4 vertex; layout(location = 1) in vec3 normal; layout(location = 2) in vec2 textureCoords; @endcode -@requires_gl (for explicit attribute location instead of using - bindAttributeLocation()) +@requires_gl (for explicit input attribute location instead of using + bindAttributeLocation()) +@requires_gl (for explicit output attribute location or using + bindFragmentDataLocation()) +@requires_gl30 Extension @extension{EXT,gpu_shader4} (for using + bindFragmentDataLocation()) @requires_gl33 Extension @extension{ARB,explicit_attrib_location} (for explicit attribute location instead of using bindAttributeLocation()) @@ -100,7 +104,7 @@ bindAttributeLocation(TextureCoords::Location, "textureCoords"); // Link... @endcode -@subsection AbstractShaderProgramTextureLayer Binding texture layer uniforms +@subsection AbstractShaderProgram-texture-layer Binding texture layer uniforms The preferred workflow is to specify texture layers directly in the shader code, e.g.: @code @@ -120,7 +124,7 @@ setUniform(uniformLocation("diffuseTexture"), DiffuseTextureLayer); setUniform(uniformLocation("specularTexture"), SpecularTextureLayer); @endcode -@section AbstractShaderProgramRenderingWorkflow Rendering workflow +@section AbstractShaderProgram-rendering-workflow Rendering workflow Basic workflow with %AbstractShaderProgram subclasses is: instancing the class (once at the beginning), then in Object::draw() reimplementation calling @@ -238,7 +242,7 @@ class MAGNUM_EXPORT AbstractShaderProgram { * before link(). * @deprecated Preferred usage is to specify attribute location * explicitly in the shader instead of using this function. See - * @ref AbstractShaderProgramAttributeLocation "class documentation" + * @ref AbstractShaderProgram-attribute-location "class documentation" * for more information. */ void bindAttributeLocation(GLuint location, const std::string& name); @@ -253,7 +257,7 @@ class MAGNUM_EXPORT AbstractShaderProgram { * before link(). * @deprecated Preferred usage is to specify attribute location * explicitly in the shader instead of using this function. See - * @ref AbstractShaderProgramAttributeLocation "class documentation" + * @ref AbstractShaderProgram-attribute-location "class documentation" * for more information. * @requires_gl * @requires_gl30 Extension @extension{EXT,gpu_shader4} diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index 97f6fbb3f..ce766a7ab 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -20,6 +20,7 @@ */ #include "Magnum.h" +#include "Color.h" namespace Magnum { @@ -233,8 +234,6 @@ class MAGNUM_EXPORT AbstractTexture { * * For more information about default values for unused components and * normalization see enums Components and ComponentType. - * @todo ES2 - GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_ALPHA? They are - * deprecated everywhere else. */ enum class Format: GLenum { #ifndef MAGNUM_TARGET_GLES @@ -345,10 +344,12 @@ class MAGNUM_EXPORT AbstractTexture { * Three-component RGB, unsigned normalized, red and blue 5bit, * green 6bit, 16bit total. */ - RGB565 = GL_RGB565, + RGB565 = GL_RGB565 #endif #ifndef MAGNUM_TARGET_GLES + , + /** * Three-component RGB, unsigned with exponent, each component * 9bit, exponent 5bit, 32bit total. @@ -408,7 +409,6 @@ class MAGNUM_EXPORT AbstractTexture { * @requires_gl30 Extension @extension{EXT,texture_compression_rgtc} */ CompressedRtgcSignedRedGreen = GL_COMPRESSED_SIGNED_RG_RGTC2, - #endif #if defined(GL_COMPRESSED_RGBA_BPTC_UNORM) || defined(DOXYGEN_GENERATING_OUTPUT) /** @@ -440,22 +440,30 @@ class MAGNUM_EXPORT AbstractTexture { CompressedBptcRGBUnsignedFloat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, #endif - /** Depth component. */ + /** + * Depth component, at least 16bit. + * + * Prefer to use the exactly specified version of this format, in + * this case e.g. `Format::Depth16`. + * @requires_gl + */ Depth = GL_DEPTH_COMPONENT, - #ifndef MAGNUM_TARGET_GLES /** - * Depth and stencil component. + * Depth and stencil component, at least 24bit depth and 8bit + * stencil. + * + * Prefer to use the exactly specified version of this format, in + * this case e.g. `Format::Depth24Stencil8`. * @requires_gl */ DepthStencil = GL_DEPTH_STENCIL, - #endif - /** 16bit depth component. */ - Depth16 = GL_DEPTH_COMPONENT16 - - #ifndef MAGNUM_TARGET_GLES - , + /** + * 16bit depth component. + * @requires_gl + */ + Depth16 = GL_DEPTH_COMPONENT16, /** * 24bit depth component. @@ -611,7 +619,7 @@ class MAGNUM_EXPORT AbstractTexture { * to `ClampToBorder`. * @requires_gl */ - inline void setBorderColor(const Vector4& color) { + inline void setBorderColor(const Color4& color) { bind(); glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, color.data()); } diff --git a/src/BufferedTexture.h b/src/BufferedTexture.h index 591ef3c11..fca8f74fc 100644 --- a/src/BufferedTexture.h +++ b/src/BufferedTexture.h @@ -49,18 +49,27 @@ class BufferedTexture { public: /** @{ @name Internal buffered texture formats */ - /** @copydoc Renderbuffer::Components */ + /** + * @copybrief AbstractTexture::Components + * + * Like AbstractTexture::Components, without three-component RGB. + */ enum class Components { Red, RedGreen, RGBA }; - /** @copydoc Renderbuffer::ComponentType */ + /** + * @copybrief AbstractTexture::ComponentType + * + * Like AbstractTexture::ComponentType, without normalized signed + * types. + */ enum class ComponentType { UnsignedByte, Byte, UnsignedShort, Short, UnsignedInt, Int, Half, Float, NormalizedUnsignedByte, NormalizedUnsignedShort }; - /** @copydoc AbstractTexture::Format */ + /** @copybrief AbstractTexture::Format */ enum class Format: GLenum { /** * Three-component RGB, float, each component 32bit, 96bit total. @@ -89,7 +98,7 @@ class BufferedTexture { /** @copydoc AbstractTexture::InternalFormat */ class MAGNUM_EXPORT InternalFormat { public: - /** @copydoc AbstractTexture::InternalFormat::InternalFormat(AbstractTexture::Components, AbstractTexture::ComponentType) */ + /** @copybrief AbstractTexture::InternalFormat::InternalFormat(AbstractTexture::Components, AbstractTexture::ComponentType) */ InternalFormat(Components components, ComponentType type); /** @copydoc AbstractTexture::InternalFormat::InternalFormat(AbstractTexture::Format) */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7f4379482..026bbcfbe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ set(Magnum_HEADERS BufferedTexture.h Buffer.h Camera.h + Color.h CubeMapTextureArray.h CubeMapTexture.h Framebuffer.h diff --git a/src/Camera.cpp b/src/Camera.cpp index 32c44787e..f15454a27 100644 --- a/src/Camera.cpp +++ b/src/Camera.cpp @@ -103,7 +103,7 @@ void Camera::fixAspectRatio() { void Camera::draw() { Scene* s = scene(); - CORRADE_ASSERT(s, "Camera: cannot draw without camera attached to scene", ) + CORRADE_ASSERT(s, "Camera: cannot draw without camera attached to scene", ); Framebuffer::clear(); diff --git a/src/Color.h b/src/Color.h new file mode 100644 index 000000000..7dd384a1f --- /dev/null +++ b/src/Color.h @@ -0,0 +1,409 @@ +#ifndef Magnum_Color_h +#define Magnum_Color_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Color3, Magnum::Color4 + */ + +#include + +#include "Math/MathTypeTraits.h" +#include "Math/Math.h" +#include "Math/Vector4.h" + +namespace Magnum { + +template class Color3; + +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + +/* Convert color from HSV */ +template inline typename std::enable_if::value, Color3>::type fromHSV(typename Color3::HSV hsv) { + T hue, saturation, value; + std::tie(hue, saturation, value) = hsv; + + /* Remove repeats */ + hue -= int(hue/T(360))*T(360); + if(hue < T(0)) hue += T(360); + + int h = int(hue/T(60)) % 6; + T f = hue/T(60) - h; + + T p = value * (T(1) - saturation); + T q = value * (T(1) - f*saturation); + T t = value * (T(1) - (T(1) - f)*saturation); + + switch(h) { + case 0: return {value, t, p}; + case 1: return {q, value, p}; + case 2: return {p, value, t}; + case 3: return {p, q, value}; + case 4: return {t, p, value}; + case 5: return {value, p, q}; + default: + CORRADE_ASSERT(false, "It shouldn't get here.", {}); + } +} +template inline typename std::enable_if::value, Color3>::type fromHSV(typename Color3::HSV hsv) { + return Color3::fromNormalized(fromHSV::FloatingPointType>(hsv)); +} + +/* Internal hue computing function */ +template T hue(const Color3& color, T max, T delta) { + T deltaInv60 = T(60)/delta; + + T hue(0); + if(delta != T(0)) { + if(max == color.r()) + hue = (color.g()-color.b())*deltaInv60 + (color.g() < color.b() ? T(360) : T(0)); + else if(max == color.g()) + hue = (color.b()-color.r())*deltaInv60 + T(120); + else /* max == color.b() */ + hue = (color.r()-color.g())*deltaInv60 + T(240); + } + + return hue; +} + +/* Hue, saturation, value for floating-point types */ +template inline T hue(typename std::enable_if::value, const Color3&>::type color) { + T max = color.max(); + T delta = max - color.min(); + return hue(color, max, delta); +} +template inline T saturation(typename std::enable_if::value, const Color3&>::type color) { + T max = color.max(); + T delta = max - color.min(); + return max != T(0) ? delta/max : T(0); +} +template inline T value(typename std::enable_if::value, const Color3&>::type color) { + return color.max(); +} + +/* Hue, saturation, value for integral types */ +template inline typename Color3::FloatingPointType hue(typename std::enable_if::value, const Color3&>::type color) { + return hue::FloatingPointType>(Color3::FloatingPointType>::fromDenormalized(color)); +} +template inline typename Color3::FloatingPointType saturation(typename std::enable_if::value, const Color3&>::type& color) { + return saturation::FloatingPointType>(Color3::FloatingPointType>::fromDenormalized(color)); +} +template inline typename Color3::FloatingPointType value(typename std::enable_if::value, const Color3&>::type color) { + return Math::normalize::FloatingPointType>(color.max()); +} + +/* Convert color to HSV */ +template inline typename Color3::HSV toHSV(typename std::enable_if::value, const Color3&>::type color) { + T max = color.max(); + T delta = max - color.min(); + + return typename Color3::HSV(hue::FloatingPointType>(color, max, delta), max != T(0) ? delta/max : T(0), max); +} +template inline typename Color3::HSV toHSV(typename std::enable_if::value, const Color3&>::type color) { + return toHSV::FloatingPointType>(Color3::FloatingPointType>::fromDenormalized(color)); +} + +/* Default alpha value */ +template inline constexpr typename std::enable_if::value, T>::type defaultAlpha() { + return T(1); +} +template inline constexpr typename std::enable_if::value, T>::type defaultAlpha() { + return std::numeric_limits::max(); +} + +} +#endif + +/** +@brief Three-component (RGB) color + +The class can store both floating-point (normalized) and integral +(denormalized) representation of color. You can convert between these two +representations using fromNormalized() and fromDenormalized(). + +Conversion from and to HSV is done always using floating-point types, so hue +is always in range in range @f$ [0.0, 360.0] @f$, saturation and value in +range @f$ [0.0, 1.0] @f$. + +@see Color4 +*/ +template class Color3: public Math::Vector3 { + public: + /** @brief Corresponding floating-point type for HSV computation */ + typedef typename Math::MathTypeTraits::FloatingPointType FloatingPointType; + + /** + * @brief Type for storing HSV values + * + * Hue in range @f$ [0.0, 360.0] @f$, saturation and value in + * range @f$ [0.0, 1.0] @f$. + */ + typedef std::tuple HSV; + + /** + * @brief Create integral color from floating-point color + * + * E.g. `{0.294118, 0.45098, 0.878431}` is converted to + * `{75, 115, 224}`, if resulting type is `unsigned char`. + * + * @note This function is enabled only if source type is floating-point + * and destination type is integral. + */ + template inline constexpr static typename std::enable_if::value && std::is_floating_point::value, Color3>::type fromNormalized(const Color3& color) { + return Color3(Math::denormalize(color.r()), + Math::denormalize(color.g()), + Math::denormalize(color.b())); + } + + /** + * @brief Create floating-point color from integral color + * + * E.g. `{75, 115, 224}` is converted to + * `{0.294118, 0.45098, 0.878431}`, if source type is `unsigned char`. + * + * @note This function is enabled only if source type is integral + * and destination type is floating-point. + */ + template inline constexpr static typename std::enable_if::value && std::is_integral::value, Color3>::type fromDenormalized(const Color3& color) { + return Color3(Math::normalize(color.r()), + Math::normalize(color.g()), + Math::normalize(color.b())); + } + + /** + * @brief Create RGB color from HSV representation + * @param hsv Hue, saturation and value + * + * Hue can overflow the range @f$ [0.0, 360.0] @f$. + */ + inline constexpr static Color3 fromHSV(HSV hsv) { + return Implementation::fromHSV(hsv); + } + /** @overload */ + inline constexpr static Color3 fromHSV(FloatingPointType hue, FloatingPointType saturation, FloatingPointType value) { + return fromHSV(std::make_tuple(hue, saturation, value)); + } + + /** + * @brief Default constructor + * + * All components are set to zero. + */ + inline constexpr Color3() {} + + /** + * @brief Gray constructor + * @param rgb RGB value + */ + inline constexpr explicit Color3(T rgb): Math::Vector3(rgb) {} + + /** @copydoc Math::Vector::Vector(const Vector&) */ + inline constexpr Color3(const Math::Vector<3, T>& other): Math::Vector3(other) {} + + /** + * @brief Constructor + * @param r R value + * @param g G value + * @param b B value + */ + inline constexpr Color3(T r, T g, T b): Math::Vector3(r, g, b) {} + + inline constexpr T r() const { return Math::Vector3::x(); } /**< @brief R component */ + inline constexpr T g() const { return Math::Vector3::y(); } /**< @brief G component */ + inline constexpr T b() const { return Math::Vector3::z(); } /**< @brief B component */ + + inline void setR(T value) { Math::Vector3::setX(value); } /**< @brief Set R component */ + inline void setG(T value) { Math::Vector3::setY(value); } /**< @brief Set G component */ + inline void setB(T value) { Math::Vector3::setZ(value); } /**< @brief Set B component */ + + /** + * @brief Convert to HSV + * + * Example usage: + * @code + * T hue, saturation, value; + * std::tie(hue, saturation, value) = color.toHSV(); + * @endcode + * + * @see hue(), saturation(), value(), fromHSV() + */ + inline constexpr HSV toHSV() const { + return Implementation::toHSV(*this); + } + + /** + * @brief Hue + * @return Hue in range @f$ [0.0, 360.0] @f$. + * + * @see saturation(), value(), toHSV(), fromHSV() + */ + inline constexpr FloatingPointType hue() const { + return Implementation::hue(*this); + } + + /** + * @brief Saturation + * @return Saturation in range @f$ [0.0, 1.0] @f$. + * + * @see hue(), value(), toHSV(), fromHSV() + */ + inline constexpr FloatingPointType saturation() const { + return Implementation::saturation(*this); + } + + /** + * @brief Value + * @return Value in range @f$ [0.0, 1.0] @f$. + * + * @see hue(), saturation(), toHSV(), fromHSV() + */ + inline constexpr FloatingPointType value() const { + return Implementation::value(*this); + } +}; + +/** +@brief Four-component (RGBA) color + +See Color3 for more information. +*/ +template class Color4: public Math::Vector4 { + public: + /** @copydoc Color3::FloatingPointType */ + typedef typename Color3::FloatingPointType FloatingPointType; + + /** @copydoc Color3::HSV */ + typedef typename Color3::HSV HSV; + + /** @copydoc Color3::fromNormalized() */ + template inline constexpr static typename std::enable_if::value && std::is_floating_point::value, Color4>::type fromNormalized(const Color4& color) { + return Color4(Math::denormalize(color.r()), + Math::denormalize(color.g()), + Math::denormalize(color.b()), + Math::denormalize(color.a())); + } + + /** @copydoc Color3::fromDenormalized() */ + template inline constexpr static typename std::enable_if::value && std::is_integral::value, Color4>::type fromDenormalized(const Color4& color) { + return Color4(Math::normalize(color.r()), + Math::normalize(color.g()), + Math::normalize(color.b()), + Math::normalize(color.a())); + } + + /** + * @copydoc Color3::fromHSV() + * @param a Alpha value, defaults to 1.0 for floating-point types + * and maximum positive value for integral types. + */ + inline constexpr static Color4 fromHSV(HSV hsv, T a = Implementation::defaultAlpha()) { + return Color4(Implementation::fromHSV(hsv), a); + } + /** @overload */ + inline constexpr static Color4 fromHSV(FloatingPointType hue, FloatingPointType saturation, FloatingPointType value, T alpha) { + return fromHSV(std::make_tuple(hue, saturation, value), alpha); + } + + /** + * @brief Default constructor + * + * RGB components are set to zero, A component is set to 1.0 for + * floating-point types and maximum positive value for integral types. + */ + inline constexpr Color4(): Math::Vector4(T(0), T(0), T(0), Implementation::defaultAlpha()) {} + + /** + * @copydoc Color3::Color3(T) + * @param alpha Alpha value, defaults to 1.0 for floating-point types + * and maximum positive value for integral types. + */ + inline constexpr explicit Color4(T rgb, T alpha = Implementation::defaultAlpha()): Math::Vector4(rgb, rgb, rgb, alpha) {} + + /** @copydoc Math::Vector::Vector(const Vector&) */ + inline constexpr Color4(const Math::Vector<4, T>& other): Math::Vector4(other) {} + + /** + * @brief Constructor + * @param r R value + * @param g G value + * @param b B value + * @param a A value, defaults to 1.0 for floating-point types and + * maximum positive value for integral types. + */ + inline constexpr Color4(T r, T g, T b, T a = Implementation::defaultAlpha()): Math::Vector4(r, g, b, a) {} + + /** + * @brief Constructor + * @param rgb Three-component color + * @param a A value + */ + /* Not marked as explicit, because conversion from Color3 to Color4 + is fairly common, nearly always with A set to 1 */ + inline constexpr Color4(const Math::Vector<3, T>& rgb, T a = Implementation::defaultAlpha()): Math::Vector4(rgb[0], rgb[1], rgb[2], a) {} + + inline constexpr T r() const { return Math::Vector4::x(); } /**< @brief R component */ + inline constexpr T g() const { return Math::Vector4::y(); } /**< @brief G component */ + inline constexpr T b() const { return Math::Vector4::z(); } /**< @brief B component */ + inline constexpr T a() const { return Math::Vector4::w(); } /**< @brief A component */ + + inline void setR(T value) { Math::Vector4::setX(value); } /**< @brief Set R component */ + inline void setG(T value) { Math::Vector4::setY(value); } /**< @brief Set G component */ + inline void setB(T value) { Math::Vector4::setZ(value); } /**< @brief Set B component */ + inline void setA(T value) { Math::Vector4::setW(value); } /**< @brief Set A component */ + + /** + * @brief RGB part of the vector + * @return First three components of the vector + * + * @see swizzle() + */ + inline constexpr Color3 rgb() const { return Math::Vector4::xyz(); } + + /** @copydoc Color3::toHSV() */ + inline constexpr HSV toHSV() const { + return Implementation::toHSV(rgb()); + } + + /** @copydoc Color3::hue() */ + inline constexpr FloatingPointType hue() const { + return Implementation::hue(rgb()); + } + + /** @copydoc Color3::saturation() */ + inline constexpr FloatingPointType saturation() const { + return Implementation::saturation(rgb()); + } + + /** @copydoc Color3::value() */ + inline constexpr FloatingPointType value() const { + return Implementation::value(rgb()); + } +}; + +/** @debugoperator{Color3} */ +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Color3& value) { + return debug << static_cast&>(value); +} + +/** @debugoperator{Color4} */ +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Color4& value) { + return debug << static_cast&>(value); +} + +} + +#endif diff --git a/src/Framebuffer.h b/src/Framebuffer.h index f88a47637..27ee0cb85 100644 --- a/src/Framebuffer.h +++ b/src/Framebuffer.h @@ -23,6 +23,7 @@ #include "BufferedImage.h" #include "CubeMapTexture.h" +#include "Color.h" #include "Image.h" #include "Renderbuffer.h" @@ -192,7 +193,7 @@ class MAGNUM_EXPORT Framebuffer { * * Initial value is `{0.0f, 0.0f, 0.0f, 1.0f}`. */ - inline static void setClearColor(const Vector4& color) { + inline static void setClearColor(const Color4& color) { glClearColor(color.r(), color.g(), color.b(), color.a()); } diff --git a/src/IndexedMesh.cpp b/src/IndexedMesh.cpp index 76d1b5040..05a9b009a 100644 --- a/src/IndexedMesh.cpp +++ b/src/IndexedMesh.cpp @@ -41,7 +41,7 @@ void IndexedMesh::draw() { void IndexedMesh::finalize() { if(isFinalized()) return; - CORRADE_ASSERT(_indexCount, "IndexedMesh: the mesh has zero index count!", ) + CORRADE_ASSERT(_indexCount, "IndexedMesh: the mesh has zero index count!", ); /* Finalize attribute positions */ Mesh::finalize(); diff --git a/src/Math/Geometry/Distance.h b/src/Math/Geometry/Distance.h index e3e710f03..143ab8784 100644 --- a/src/Math/Geometry/Distance.h +++ b/src/Math/Geometry/Distance.h @@ -52,7 +52,7 @@ class Distance { * values, because it doesn't compute the square root. */ template static T linePointSquared(const Vector3& a, const Vector3& b, const Vector3& point) { - return Vector3::cross(point - a, point - b).lengthSquared()/(b - a).lengthSquared(); + return Vector3::cross(point - a, point - b).dot()/(b - a).dot(); } /** @@ -97,9 +97,9 @@ class Distance { template static T lineSegmentPointSquared(const Vector3& a, const Vector3& b, const Vector3& point) { Vector3 pointMinusA = point - a; Vector3 pointMinusB = point - b; - T pointDistanceA = pointMinusA.lengthSquared(); - T pointDistanceB = pointMinusB.lengthSquared(); - T bDistanceA = (b - a).lengthSquared(); + T pointDistanceA = pointMinusA.dot(); + T pointDistanceB = pointMinusB.dot(); + T bDistanceA = (b - a).dot(); /* Point is before A */ if(pointDistanceB > bDistanceA + pointDistanceA) @@ -110,7 +110,7 @@ class Distance { return pointDistanceB; /* Between A and B */ - return Vector3::cross(pointMinusA, pointMinusB).lengthSquared()/bDistanceA; + return Vector3::cross(pointMinusA, pointMinusB).dot()/bDistanceA; } }; diff --git a/src/Math/Math.h b/src/Math/Math.h index c0bb71cd3..7c3955bed 100644 --- a/src/Math/Math.h +++ b/src/Math/Math.h @@ -16,6 +16,9 @@ */ #include +#include +#include +#include #include "magnumCompatibility.h" #include "magnumVisibility.h" @@ -86,6 +89,43 @@ template inline constexpr T pow(T base) { */ size_t MAGNUM_EXPORT log(size_t base, size_t number); +/** +@brief Normalize floating-point value + +Converts integral value from full range of given (signed/unsigned) integral +type to value in range @f$ [0, 1] @f$. + +@attention To ensure the integral type is correctly detected when using +literals, this function should be called with both template parameters +explicit, e.g.: +@code +// Even if this is char literal, integral type is `int`, thus a = 0.1f +float a = normalize('\127'); + +// b = 1.0f +float b = normalize('\127'); +@endcode +*/ +template inline constexpr typename std::enable_if::value && std::is_integral::value, FloatingPoint>::type normalize(Integral value) { + return (FloatingPoint(value)-FloatingPoint(std::numeric_limits::min()))/ + (FloatingPoint(std::numeric_limits::max()) - FloatingPoint(std::numeric_limits::min())); +} + +/** +@brief Denormalize floating-point value + +Converts floating-point value in range @f$ [0, 1] @f$ to full range of given +integral type. + +@note For best precision, `FloatingPoint` type should be always larger that +resulting `Integral` type (e.g. `double` to `int`, `long double` to `long long`). +*/ +template inline constexpr typename std::enable_if::value && std::is_integral::value, Integral>::type denormalize(FloatingPoint value) { + return std::numeric_limits::min() + + round(FloatingPoint(value*std::numeric_limits::max()) - + FloatingPoint(value*std::numeric_limits::min())); +} + /** * @brief Angle in degrees * diff --git a/src/Math/MathTypeTraits.h b/src/Math/MathTypeTraits.h index c41634ce5..9e8a27ad0 100644 --- a/src/Math/MathTypeTraits.h +++ b/src/Math/MathTypeTraits.h @@ -53,6 +53,22 @@ support given feature, thus forcing the compilation stop with an error. */ template struct MathTypeTraits { #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief Corresponding numeric type large at least as `int` + * + * Usable e.g. to prevent conversion of `char` to characters when printing + * numeric types to output. + */ + typedef U NumericType; + + /** + * @brief Corresponding floating-point type for normalization + * + * If the type is not already floating-point, defines smallest larger + * floating-point type. + */ + typedef U FloatingPointType; + /** * @brief Epsilon value for fuzzy compare * @@ -81,39 +97,86 @@ template struct MathTypeTraits { */ #ifndef DOXYGEN_GENERATING_OUTPUT -template struct _MathTypeTraitsIntegral { +namespace Implementation { + +template struct MathTypeTraitsIntegral { inline constexpr static T epsilon() { return 1; } inline constexpr static bool equals(T a, T b) { return a == b; } }; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; +template struct MathTypeTraitsFloatingPoint { + inline static bool equals(T a, T b) { + return std::abs(a - b) < MathTypeTraits::epsilon(); + } +}; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; +template struct MathTypeTraitsLong {}; -/* long is 32 bits somewhere and 64 bits elsewhere, so it cannot be mapped to - any of them */ -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; +template<> struct MathTypeTraitsLong<8> { + typedef unsigned int UnsignedType; + typedef int Type; +}; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; -template<> struct MathTypeTraits: public _MathTypeTraitsIntegral {}; +template<> struct MathTypeTraitsLong<16> { + typedef unsigned long long UnsignedType; + typedef long long Type; +}; -template struct _MathTypeTraitsFloatingPoint { - inline static bool equals(T a, T b) { - return std::abs(a - b) < MathTypeTraits::epsilon(); - } +} + +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral { + typedef unsigned int NumericType; + typedef float FloatingPointType; +}; +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral { + typedef int NumericType; + typedef float FloatingPointType; +}; + +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral { + typedef unsigned int NumericType; + typedef float FloatingPointType; +}; +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral { + typedef int NumericType; + typedef float FloatingPointType; +}; + +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral { + typedef unsigned int NumericType; + typedef double FloatingPointType; }; -template<> struct MathTypeTraits: public _MathTypeTraitsFloatingPoint { +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral { + typedef int NumericType; + typedef double FloatingPointType; +}; + +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral { + typedef unsigned long long NumericType; + typedef long double FloatingPointType; +}; +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral { + typedef long long NumericType; + typedef long double FloatingPointType; +}; + +/* long is 32 bits somewhere and 64 bits elsewhere */ +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral::Type> {}; +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsIntegral::Type> {}; + +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsFloatingPoint { + typedef float NumericType; + typedef float FloatingPointType; + inline constexpr static float epsilon() { return FLOAT_EQUALITY_PRECISION; } }; -template<> struct MathTypeTraits: public _MathTypeTraitsFloatingPoint { +template<> struct MathTypeTraits: public Implementation::MathTypeTraitsFloatingPoint { + typedef float NumericType; + typedef double FloatingPointType; + inline constexpr static double epsilon() { return DOUBLE_EQUALITY_PRECISION; } }; #endif diff --git a/src/Math/Matrix.h b/src/Math/Matrix.h index f89032887..6ea0ed3c1 100644 --- a/src/Math/Matrix.h +++ b/src/Math/Matrix.h @@ -36,6 +36,8 @@ namespace Implementation { * @todo first col, then row (cache adjacency) */ template class Matrix { + static_assert(size != 0, "Matrix cannot have zero elements"); + friend class Matrix; /* for ij() */ public: @@ -264,7 +266,7 @@ template Corrade::Utility::Debug operator<<(Corrade::Utili if(row != 0) debug << ",\n "; for(size_t col = 0; col != size; ++col) { if(col != 0) debug << ", "; - debug << value[col][row]; + debug << typename MathTypeTraits::NumericType(value[col][row]); } } debug << ')'; diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index 39c2ced26..2f127d2bc 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -75,8 +75,8 @@ template class Matrix4: public Matrix<4, T> { static Matrix4 rotation(T angle, const Vector3& vec) { Vector3 vn = vec.normalized(); - T sine = sin(angle); - T cosine = cos(angle); + T sine = std::sin(angle); + T cosine = std::cos(angle); T oneMinusCosine = T(1) - cosine; T xx = vn.x()*vn.x(); diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index 6dbf2abc9..9e336887f 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -1,6 +1,7 @@ corrade_add_test2(MathMathTypeTraitsTest MathTypeTraitsTest.cpp) corrade_add_test2(MathVectorTest VectorTest.cpp) +set_target_properties(MathVectorTest PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) corrade_add_test2(MathVector2Test Vector2Test.cpp) corrade_add_test2(MathVector3Test Vector3Test.cpp) corrade_add_test2(MathVector4Test Vector4Test.cpp) diff --git a/src/Math/Test/MathTest.cpp b/src/Math/Test/MathTest.cpp index 46babe120..db4d29676 100644 --- a/src/Math/Test/MathTest.cpp +++ b/src/Math/Test/MathTest.cpp @@ -17,12 +17,16 @@ #include "Math.h" +using namespace std; + CORRADE_TEST_MAIN(Magnum::Math::Test::MathTest) namespace Magnum { namespace Math { namespace Test { MathTest::MathTest() { addTests(&MathTest::degrad, + &MathTest::normalize, + &MathTest::denormalize, &MathTest::pow, &MathTest::log); } @@ -33,6 +37,56 @@ void MathTest::degrad() { CORRADE_COMPARE(rad(Constants::pi()/2), Constants::pi()/2); } +void MathTest::normalize() { + /* Range for signed and unsigned */ + CORRADE_COMPARE((Math::normalize(-128)), 0.0f); + CORRADE_COMPARE((Math::normalize(127)), 1.0f); + CORRADE_COMPARE((Math::normalize(0)), 0.0f); + CORRADE_COMPARE((Math::normalize(255)), 1.0f); + + /* Between */ + CORRADE_COMPARE((Math::normalize(16384)), 0.750011f); + CORRADE_COMPARE((Math::normalize(-16384)), 0.250004f); + + /* Test overflow for large types */ + CORRADE_COMPARE((Math::normalize(numeric_limits::min())), 0.0f); + CORRADE_COMPARE((Math::normalize(numeric_limits::max())), 1.0f); + CORRADE_COMPARE((Math::normalize(0)), 0.0f); + CORRADE_COMPARE((Math::normalize(numeric_limits::max())), 1.0f); + + CORRADE_COMPARE((Math::normalize(numeric_limits::min())), 0.0); + CORRADE_COMPARE((Math::normalize(numeric_limits::max())), 1.0); + CORRADE_COMPARE((Math::normalize(0)), 0.0); + CORRADE_COMPARE((Math::normalize(numeric_limits::max())), 1.0); +} + +void MathTest::denormalize() { + /* Range for signed and unsigned */ + CORRADE_COMPARE(Math::denormalize(0.0f), -128); + CORRADE_COMPARE(Math::denormalize(1.0f), 127); + CORRADE_COMPARE(Math::denormalize(0.0f), 0); + CORRADE_COMPARE(Math::denormalize(1.0f), 255); + + /* Between */ + CORRADE_COMPARE(Math::denormalize(0.33f), -11141); + CORRADE_COMPARE(Math::denormalize(0.66f), 10485); + + /* Test overflow for large types */ + CORRADE_COMPARE(Math::denormalize(0.0f), numeric_limits::min()); + CORRADE_COMPARE(Math::denormalize(0.0f), 0); + CORRADE_COMPARE(Math::denormalize(0.0), numeric_limits::min()); + CORRADE_COMPARE(Math::denormalize(0.0), 0); + + CORRADE_COMPARE(Math::denormalize(1.0), numeric_limits::max()); + CORRADE_COMPARE(Math::denormalize(1.0), numeric_limits::max()); + + { + CORRADE_EXPECT_FAIL("Denormalize doesn't work for large types well"); + CORRADE_COMPARE((Math::denormalize(1.0)), numeric_limits::max()); + CORRADE_COMPARE((Math::denormalize(1.0)), numeric_limits::max()); + } +} + void MathTest::pow() { CORRADE_COMPARE(Math::pow<10>(2ul), 1024ul); CORRADE_COMPARE(Math::pow<0>(3ul), 1ul); diff --git a/src/Math/Test/MathTest.h b/src/Math/Test/MathTest.h index 548b01e3c..b6dcba0c8 100644 --- a/src/Math/Test/MathTest.h +++ b/src/Math/Test/MathTest.h @@ -24,6 +24,8 @@ class MathTest: public Corrade::TestSuite::Tester { MathTest(); void degrad(); + void normalize(); + void denormalize(); void pow(); void log(); }; diff --git a/src/Math/Test/VectorTest.cpp b/src/Math/Test/VectorTest.cpp index 965a4c871..327c2cb49 100644 --- a/src/Math/Test/VectorTest.cpp +++ b/src/Math/Test/VectorTest.cpp @@ -37,10 +37,13 @@ VectorTest::VectorTest() { &VectorTest::dot, &VectorTest::multiplyDivide, &VectorTest::addSubstract, + &VectorTest::dotSelf, &VectorTest::length, - &VectorTest::lengthSquared, &VectorTest::normalized, + &VectorTest::sum, &VectorTest::product, + &VectorTest::min, + &VectorTest::max, &VectorTest::angle, &VectorTest::negative, &VectorTest::debug); @@ -109,24 +112,43 @@ void VectorTest::addSubstract() { CORRADE_COMPARE(expected - b, a); } -void VectorTest::length() { - CORRADE_COMPARE(Vector4(1.0f, 2.0f, 3.0f, 4.0f).length(), 5.4772256f); +void VectorTest::dotSelf() { + CORRADE_COMPARE(Vector4(1.0f, 2.0f, 3.0f, 4.0f).dot(), 30.0f); } -void VectorTest::lengthSquared() { - CORRADE_COMPARE(Vector4(1.0f, 2.0f, 3.0f, 4.0f).lengthSquared(), 30.0f); +void VectorTest::length() { + CORRADE_COMPARE(Vector4(1.0f, 2.0f, 3.0f, 4.0f).length(), 5.4772256f); } void VectorTest::normalized() { CORRADE_COMPARE(Vector4(1.0f, 1.0f, 1.0f, 1.0f).normalized(), Vector4(0.5f, 0.5f, 0.5f, 0.5f)); } +void VectorTest::sum() { + CORRADE_COMPARE(Vector3(1.0f, 2.0f, 4.0f).sum(), 7.0f); +} + void VectorTest::product() { CORRADE_COMPARE(Vector3(1.0f, 2.0f, 3.0f).product(), 6.0f); } +void VectorTest::min() { + CORRADE_COMPARE(Vector3(1.0f, -2.0f, 3.0f).min(), -2.0f); +} + +void VectorTest::max() { + CORRADE_COMPARE(Vector3(1.0f, -2.0f, 3.0f).max(), 3.0f); +} + void VectorTest::angle() { - CORRADE_COMPARE(Vector3::angle({2.0f, 3.0f, 4.0f}, {1.0f, -2.0f, 3.0f}), rad(1.162514f)); + ostringstream o; + Error::setOutput(&o); + /* Both vectors must be normalized, otherwise NaN is returned */ + CORRADE_COMPARE(Vector3::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), {1.0f, -2.0f, 3.0f}), numeric_limits::quiet_NaN()); + CORRADE_COMPARE(o.str(), "Math::Vector::angle(): vectors must be normalized!\n"); + CORRADE_COMPARE(Vector3::angle({2.0f, 3.0f, 4.0f}, Vector3(1.0f, -2.0f, 3.0f).normalized()), numeric_limits::quiet_NaN()); + + CORRADE_COMPARE(Vector3::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), Vector3(1.0f, -2.0f, 3.0f).normalized()), rad(1.162514f)); } void VectorTest::negative() { diff --git a/src/Math/Test/VectorTest.h b/src/Math/Test/VectorTest.h index c9b475e1c..662865119 100644 --- a/src/Math/Test/VectorTest.h +++ b/src/Math/Test/VectorTest.h @@ -29,10 +29,13 @@ class VectorTest: public Corrade::TestSuite::Tester { void dot(); void multiplyDivide(); void addSubstract(); + void dotSelf(); void length(); - void lengthSquared(); void normalized(); + void sum(); void product(); + void min(); + void max(); void angle(); void negative(); diff --git a/src/Math/Vector.h b/src/Math/Vector.h index 7bd884fbc..bfdb1681f 100644 --- a/src/Math/Vector.h +++ b/src/Math/Vector.h @@ -20,6 +20,7 @@ */ #include +#include #include #include "MathTypeTraits.h" @@ -42,6 +43,8 @@ namespace Implementation { /** @brief %Vector */ template class Vector { + static_assert(size != 0, "Vector cannot have zero elements"); + public: const static size_t Size = size; /**< @brief %Vector size */ typedef T Type; /**< @brief %Vector data type */ @@ -68,6 +71,7 @@ template class Vector { * @f[ * a \cdot b = \sum_{i=0}^{n-1} a_ib_i * @f] + * @see dot() const */ static T dot(const Vector& a, const Vector& b) { T out(0); @@ -79,16 +83,18 @@ template class Vector { } /** - * @brief Angle between vectors + * @brief Angle between normalized vectors * * @f[ * \phi = \frac{a \cdot b}{|a| \cdot |b|} * @f] - * - * @todo optimize - Assume the vectors are normalized? + * @attention If any of the parameters is not normalized (and + * assertions are enabled), returns NaN. */ inline static T angle(const Vector& a, const Vector& b) { - return acos(dot(a, b)/(a.length()*b.length())); + CORRADE_ASSERT(MathTypeTraits::equals(a.dot(), T(1)) && MathTypeTraits::equals(b.dot(), T(1)), + "Math::Vector::angle(): vectors must be normalized!", std::numeric_limits::quiet_NaN()); + return std::acos(dot(a, b)); } /** @brief Default constructor */ @@ -241,24 +247,26 @@ template class Vector { } /** - * @brief %Vector length + * @brief Dot product of the vector + * + * Should be used instead of length() for comparing vector length with + * other values, because it doesn't compute the square root, just the + * dot product: @f$ a \cdot a < length \cdot length @f$ is faster + * than @f$ \sqrt{a \cdot a} < length @f$. * - * @see lengthSquared() + * @see dot(const Vector&, const Vector&) */ - inline T length() const { - return sqrt(dot(*this, *this)); + inline T dot() const { + return dot(*this, *this); } /** - * @brief %Vector length squared + * @brief %Vector length * - * More efficient than length() for comparing vector length with - * other values, because it doesn't compute the square root, just the - * dot product: @f$ a \cdot a < length \cdot length @f$ is faster - * than @f$ \sqrt{a \cdot a} < length @f$. + * @see dot() const */ - inline T lengthSquared() const { - return dot(*this, *this); + inline T length() const { + return std::sqrt(dot()); } /** @brief Normalized vector (of length 1) */ @@ -266,9 +274,19 @@ template class Vector { return *this/length(); } + /** @brief Sum of values in the vector */ + T sum() const { + T out(0); + + for(size_t i = 0; i != size; ++i) + out += (*this)[i]; + + return out; + } + /** @brief Product of values in the vector */ T product() const { - T out = 1; + T out(1); for(size_t i = 0; i != size; ++i) out *= (*this)[i]; @@ -276,6 +294,26 @@ template class Vector { return out; } + /** @brief Minimal value in the vector */ + T min() const { + T out((*this)[0]); + + for(size_t i = 1; i != size; ++i) + out = std::min(out, (*this)[i]); + + return out; + } + + /** @brief Maximal value in the vector */ + T max() const { + T out((*this)[0]); + + for(size_t i = 1; i != size; ++i) + out = std::max(out, (*this)[i]); + + return out; + } + private: T _data[size]; }; @@ -286,7 +324,7 @@ template Corrade::Utility::Debug operator<<(Corrade::Utili debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); for(size_t i = 0; i != size; ++i) { if(i != 0) debug << ", "; - debug << value[i]; + debug << typename MathTypeTraits::NumericType(value[i]); } debug << ')'; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); diff --git a/src/Math/Vector3.h b/src/Math/Vector3.h index 30072d7f5..d70e6dc08 100644 --- a/src/Math/Vector3.h +++ b/src/Math/Vector3.h @@ -49,26 +49,29 @@ template class Vector3: public Vector<3, T> { a[0]*b[1]-a[1]*b[0]); } + /** @copydoc Vector::Vector() */ + inline constexpr Vector3() {} + /** @copydoc Vector::Vector(T) */ - inline constexpr explicit Vector3(T value = T()): Vector<3, T>(value, value, value) {} + inline constexpr explicit Vector3(T value): Vector<3, T>(value, value, value) {} /** @copydoc Vector::Vector(const Vector&) */ inline constexpr Vector3(const Vector<3, T>& other): Vector<3, T>(other) {} /** * @brief Constructor - * @param x X / R value - * @param y Y / G value - * @param z Z / B value + * @param x X value + * @param y Y value + * @param z Z value */ inline constexpr Vector3(T x, T y, T z): Vector<3, T>(x, y, z) {} /** * @brief Constructor - * @param other Two component vector - * @param z Z / B value + * @param xy Two component vector + * @param z Z value */ - inline constexpr Vector3(const Vector<2, T>& other, T z): Vector<3, T>(other[0], other[1], z) {} + inline constexpr Vector3(const Vector<2, T>& xy, T z): Vector<3, T>(xy[0], xy[1], z) {} inline constexpr T x() const { return (*this)[0]; } /**< @brief X component */ inline constexpr T y() const { return (*this)[1]; } /**< @brief Y component */ @@ -86,14 +89,6 @@ template class Vector3: public Vector<3, T> { */ inline constexpr Vector2 xy() const { return Vector2::from(Vector<3, T>::data()); } - inline constexpr T r() const { return x(); } /**< @brief R component */ - inline constexpr T g() const { return x(); } /**< @brief G component */ - inline constexpr T b() const { return z(); } /**< @brief B component */ - - inline void setR(T value) { setX(value); } /**< @brief Set R component */ - inline void setG(T value) { setY(value); } /**< @brief Set G component */ - inline void setB(T value) { setZ(value); } /**< @brief Set B component */ - MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector3, 3) }; diff --git a/src/Math/Vector4.h b/src/Math/Vector4.h index c406ef19b..32d4f3c51 100644 --- a/src/Math/Vector4.h +++ b/src/Math/Vector4.h @@ -29,7 +29,7 @@ template class Vector4: public Vector<4, T> { /** * @copydoc Vector::Vector * - * W / A component is set to one. + * W component is set to one. */ inline constexpr Vector4(): Vector<4, T>(T(0), T(0), T(0), T(1)) {} @@ -41,21 +41,21 @@ template class Vector4: public Vector<4, T> { /** * @brief Constructor - * @param x X / R value - * @param y Y / G value - * @param z Z / B value - * @param w W / A value + * @param x X value + * @param y Y value + * @param z Z value + * @param w W value */ inline constexpr Vector4(T x, T y, T z, T w = T(1)): Vector<4, T>(x, y, z, w) {} /** * @brief Constructor - * @param other Three component vector - * @param w W / A value + * @param xyz Three component vector + * @param w W value */ /* Not marked as explicit, because conversion from Vector3 to Vector4 is fairly common, nearly always with W set to 1 */ - inline constexpr Vector4(const Vector<3, T>& other, T w = T(1)): Vector<4, T>(other[0], other[1], other[2], w) {} + inline constexpr Vector4(const Vector<3, T>& xyz, T w = T(1)): Vector<4, T>(xyz[0], xyz[1], xyz[2], w) {} inline constexpr T x() const { return (*this)[0]; } /**< @brief X component */ inline constexpr T y() const { return (*this)[1]; } /**< @brief Y component */ @@ -83,24 +83,6 @@ template class Vector4: public Vector<4, T> { */ inline constexpr Vector2 xy() const { return Vector2::from(Vector<4, T>::data()); } - inline constexpr T r() const { return x(); } /**< @brief R component */ - inline constexpr T g() const { return y(); } /**< @brief G component */ - inline constexpr T b() const { return z(); } /**< @brief B component */ - inline constexpr T a() const { return w(); } /**< @brief A component */ - - inline void setR(T value) { setX(value); } /**< @brief Set R component */ - inline void setG(T value) { setY(value); } /**< @brief Set G component */ - inline void setB(T value) { setZ(value); } /**< @brief Set B component */ - inline void setA(T value) { setA(value); } /**< @brief Set A component */ - - /** - * @brief RGB part of the vector - * @return First three components of the vector - * - * @see swizzle() - */ - inline constexpr Vector3 rgb() const { return xyz(); } - MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector4, 4) }; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index a16cf8286..59d61c01d 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -78,7 +78,7 @@ void Mesh::finalize() { /* Already finalized */ if(finalized) return; - CORRADE_ASSERT(_vertexCount, "Mesh: the mesh has zero vertex count!", ) + CORRADE_ASSERT(_vertexCount, "Mesh: the mesh has zero vertex count!", ); /* Finalize attribute positions for every buffer */ for(auto it = _buffers.begin(); it != _buffers.end(); ++it) { diff --git a/src/MeshTools/CombineIndexedArrays.h b/src/MeshTools/CombineIndexedArrays.h index 4ed096639..d3b272e8d 100644 --- a/src/MeshTools/CombineIndexedArrays.h +++ b/src/MeshTools/CombineIndexedArrays.h @@ -57,7 +57,7 @@ class CombineIndexedArrays { private: template inline static size_t indexCount(const std::vector& first, const std::vector&... next) { - CORRADE_ASSERT(sizeof...(next) == 0 || indexCount(next...) == first.size(), "MeshTools::combineIndexedArrays(): index arrays don't have the same length, nothing done.", 0) + CORRADE_ASSERT(sizeof...(next) == 0 || indexCount(next...) == first.size(), "MeshTools::combineIndexedArrays(): index arrays don't have the same length, nothing done.", 0); return first.size(); } diff --git a/src/MeshTools/FlipNormals.cpp b/src/MeshTools/FlipNormals.cpp index 108d258f9..b3807cfd8 100644 --- a/src/MeshTools/FlipNormals.cpp +++ b/src/MeshTools/FlipNormals.cpp @@ -20,7 +20,7 @@ using namespace std; namespace Magnum { namespace MeshTools { void flipFaceWinding(vector& indices) { - CORRADE_ASSERT(!(indices.size()%3), "MeshTools::flipNormals(): index count is not divisible by 3!", ) + CORRADE_ASSERT(!(indices.size()%3), "MeshTools::flipNormals(): index count is not divisible by 3!", ); for(size_t i = 0; i != indices.size(); i += 3) swap(indices[i+1], indices[i+2]); diff --git a/src/MeshTools/GenerateFlatNormals.cpp b/src/MeshTools/GenerateFlatNormals.cpp index 69d9b7105..e52e4379e 100644 --- a/src/MeshTools/GenerateFlatNormals.cpp +++ b/src/MeshTools/GenerateFlatNormals.cpp @@ -22,7 +22,7 @@ using namespace std; namespace Magnum { namespace MeshTools { tuple, vector> generateFlatNormals(const std::vector< unsigned int >& indices, const vector< Vector4 >& vertices) { - CORRADE_ASSERT(!(indices.size()%3), "MeshTools::generateFlatNormals(): index count is not divisible by 3!", (tuple, vector>())) + CORRADE_ASSERT(!(indices.size()%3), "MeshTools::generateFlatNormals(): index count is not divisible by 3!", (tuple, vector>())); /* Create normal for every triangle (assuming counterclockwise winding) */ vector normalIndices; diff --git a/src/MeshTools/Interleave.h b/src/MeshTools/Interleave.h index 8376b1ae7..aa1de8558 100644 --- a/src/MeshTools/Interleave.h +++ b/src/MeshTools/Interleave.h @@ -53,7 +53,7 @@ class Interleave { } template void operator()(Mesh* mesh, Buffer* buffer, Buffer::Usage usage, const T&... attributes) { - CORRADE_ASSERT(mesh->isInterleaved(buffer), "MeshTools::interleave(): the buffer is not interleaved, nothing done", ) + CORRADE_ASSERT(mesh->isInterleaved(buffer), "MeshTools::interleave(): the buffer is not interleaved, nothing done", ); operator()(attributes...); @@ -64,7 +64,7 @@ class Interleave { } template inline static size_t attributeCount(const T& first, const U&... next) { - CORRADE_ASSERT(sizeof...(next) == 0 || attributeCount(next...) == first.size(), "MeshTools::interleave(): attribute arrays don't have the same length, nothing done.", 0) + CORRADE_ASSERT(sizeof...(next) == 0 || attributeCount(next...) == first.size(), "MeshTools::interleave(): attribute arrays don't have the same length, nothing done.", 0); return first.size(); } diff --git a/src/MeshTools/Subdivide.h b/src/MeshTools/Subdivide.h index 444c44611..7dad9a0b7 100644 --- a/src/MeshTools/Subdivide.h +++ b/src/MeshTools/Subdivide.h @@ -32,7 +32,7 @@ template class Subdivide { inline Subdivide(std::vector& indices, std::vector& vertices): indices(indices), vertices(vertices) {} void operator()(Interpolator interpolator) { - CORRADE_ASSERT(!(indices.size()%3), "MeshTools::subdivide(): index count is not divisible by 3!", ) + CORRADE_ASSERT(!(indices.size()%3), "MeshTools::subdivide(): index count is not divisible by 3!", ); size_t indexCount = indices.size(); indices.reserve(indices.size()*4); diff --git a/src/Object.cpp b/src/Object.cpp index 861b03346..66614f7ed 100644 --- a/src/Object.cpp +++ b/src/Object.cpp @@ -64,7 +64,7 @@ Matrix4 Object::absoluteTransformation(Camera* camera) { /* We got to the scene, multiply with camera matrix */ if(p->parent() == p) { if(camera) { - CORRADE_ASSERT(camera->scene() == scene(), "Object::absoluteTransformation(): the camera is not part of the same scene as object!", t) + CORRADE_ASSERT(camera->scene() == scene(), "Object::absoluteTransformation(): the camera is not part of the same scene as object!", t); t = camera->cameraMatrix()*t; } @@ -74,7 +74,7 @@ Matrix4 Object::absoluteTransformation(Camera* camera) { p = p->parent(); } - CORRADE_ASSERT(p != nullptr || camera == nullptr, "Object::absoluteTransformation(): the object is not part of camera scene!", t) + CORRADE_ASSERT(p != nullptr || camera == nullptr, "Object::absoluteTransformation(): the object is not part of camera scene!", t); return t; } diff --git a/src/Physics/Sphere.cpp b/src/Physics/Sphere.cpp index ac9ed700d..334982b07 100644 --- a/src/Physics/Sphere.cpp +++ b/src/Physics/Sphere.cpp @@ -41,7 +41,7 @@ bool Sphere::collides(const AbstractShape* other) const { } bool Sphere::operator%(const Point& other) const { - return (other.transformedPosition()-transformedPosition()).lengthSquared() < + return (other.transformedPosition()-transformedPosition()).dot() < Math::pow<2>(transformedRadius()); } @@ -56,7 +56,7 @@ bool Sphere::operator%(const LineSegment& other) const { } bool Sphere::operator%(const Sphere& other) const { - return (other.transformedPosition()-transformedPosition()).lengthSquared() < + return (other.transformedPosition()-transformedPosition()).dot() < Math::pow<2>(transformedRadius()+other.transformedRadius()); } diff --git a/src/Primitives/Capsule.cpp b/src/Primitives/Capsule.cpp index 00303e687..5df5b13f7 100644 --- a/src/Primitives/Capsule.cpp +++ b/src/Primitives/Capsule.cpp @@ -20,7 +20,7 @@ using namespace std; namespace Magnum { namespace Primitives { Capsule::Capsule(unsigned int rings, unsigned int segments, GLfloat length, TextureCoords textureCoords): MeshData("", Mesh::Primitive::Triangles, new vector, {new vector()}, {new vector()}, textureCoords == TextureCoords::Generate ? vector*>{new vector()} : vector*>()), segments(segments), textureCoords(textureCoords) { - CORRADE_ASSERT(rings >= 1 && segments >= 3, "Capsule must have at least one ring and three segments", ) + CORRADE_ASSERT(rings >= 1 && segments >= 3, "Capsule must have at least one ring and three segments", ); GLfloat height = 2.0f+length; GLfloat textureCoordsVIncrement = 1.0f/(rings*height); diff --git a/src/Primitives/UVSphere.cpp b/src/Primitives/UVSphere.cpp index ba798f68a..825a5ee26 100644 --- a/src/Primitives/UVSphere.cpp +++ b/src/Primitives/UVSphere.cpp @@ -20,7 +20,7 @@ using namespace std; namespace Magnum { namespace Primitives { UVSphere::UVSphere(unsigned int rings, unsigned int segments, TextureCoords textureCoords): Capsule(segments, textureCoords) { - CORRADE_ASSERT(rings >= 2 && segments >= 3, "UVSphere must have at least two rings and three segments", ) + CORRADE_ASSERT(rings >= 2 && segments >= 3, "UVSphere must have at least two rings and three segments", ); GLfloat textureCoordsVIncrement = 1.0f/rings; GLfloat ringAngleIncrement = Math::Constants::pi()/rings; diff --git a/src/Query.h b/src/Query.h index a9780572b..38d4d1cf1 100644 --- a/src/Query.h +++ b/src/Query.h @@ -64,7 +64,7 @@ class MAGNUM_EXPORT AbstractQuery { * Note that this function is blocking until the result is available. * See resultAvailable(). * - * @requires_gl33 Extension @extension{ARB,timer_query} (64bit integers) + * @requires_gl33 Extension @extension{ARB,timer_query} (result type `GLuint64` and `GLint64`) */ template T result(); @@ -101,6 +101,7 @@ if(!q.resultAvailable()) { GLuint primitiveCount = q.result(); @endcode @requires_gl +@requires_gl30 Extension @extension{EXT,transform_feedback} */ class MAGNUM_EXPORT Query: public AbstractQuery { public: @@ -196,7 +197,11 @@ class MAGNUM_EXPORT SampleQuery: public AbstractQuery { AnySamplesPassed = GL_ANY_SAMPLES_PASSED }; - /** @brief Conditional render mode */ + /** + * @brief Conditional render mode + * + * @requires_gl30 Extension @extension{NV,conditional_render} + */ enum class ConditionalRenderMode: GLenum { /** * If query result is not yet available, waits for it and @@ -233,12 +238,20 @@ class MAGNUM_EXPORT SampleQuery: public AbstractQuery { /** @copydoc Query::end() */ void end(); - /** @brief Begin conditional rendering based on result value */ + /** + * @brief Begin conditional rendering based on result value + * + * @requires_gl30 Extension @extension{NV,conditional_render} + */ inline void beginConditionalRender(ConditionalRenderMode mode) { glBeginConditionalRender(query, static_cast(mode)); } - /** @brief End conditional render */ + /** + * @brief End conditional render + * + * @requires_gl30 Extension @extension{NV,conditional_render} + */ inline void endConditionalRender() { glEndConditionalRender(); } diff --git a/src/Renderbuffer.h b/src/Renderbuffer.h index 94b75a915..3a9d14145 100644 --- a/src/Renderbuffer.h +++ b/src/Renderbuffer.h @@ -41,7 +41,7 @@ class Renderbuffer { #ifndef MAGNUM_TARGET_GLES /** - * @copydoc AbstractTexture::Components + * @copybrief AbstractTexture::Components * * Like AbstractTexture::Components, without three-component RGB. * @requires_gl @@ -51,7 +51,7 @@ class Renderbuffer { }; /** - * @copydoc AbstractTexture::ComponentType + * @copybrief AbstractTexture::ComponentType * * Like AbstractTexture::ComponentType, without normalized signed * types. @@ -64,11 +64,11 @@ class Renderbuffer { #endif /** - * @copydoc AbstractTexture::Format + * @copybrief AbstractTexture::Format * * Like AbstractTexture::Format without * AbstractTexture::Format::RGB9Intensity5, three-component and - * compressed formats. + * compressed formats, but with added separate stencil index. */ enum class Format: GLenum { #ifndef MAGNUM_TARGET_GLES @@ -92,24 +92,65 @@ class Renderbuffer { RGB565 = GL_RGB565, #endif + #ifndef MAGNUM_TARGET_GLES + /** + * Depth component, at least 16bit. + * + * Prefer to use the exactly specified version of this format, in + * this case e.g. `Format::%Depth16`. + * @requires_gl Use exactly specified format Format::%Depth16 instead. + */ Depth = GL_DEPTH_COMPONENT, - #ifndef MAGNUM_TARGET_GLES DepthStencil = GL_DEPTH_STENCIL, #endif - #ifdef MAGNUM_TARGET_GLES - Depth16 = GL_DEPTH_COMPONENT16 - #else Depth16 = GL_DEPTH_COMPONENT16, + #ifndef MAGNUM_TARGET_GLES Depth24 = GL_DEPTH_COMPONENT24, DepthFloat = GL_DEPTH_COMPONENT32F, + + /** + * Stencil index (unspecified size). + * + * Prefer to use the exactly specified version of this format, in + * this case e.g. `Format::%Stencil8`. + * @requires_gl Use exactly specified format Format::%Stencil8 instead. + */ + Stencil = GL_STENCIL_INDEX, + + /** + * 1-bit stencil index. + * + * @requires_gl Use Format::%Stencil8 instead. + */ + Stencil1 = GL_STENCIL_INDEX1, + + /** + * 4-bit stencil index. + * + * @requires_gl Use Format::%Stencil8 instead. + */ + Stencil4 = GL_STENCIL_INDEX4, + #endif + + /** 8-bit stencil index. */ + Stencil8 = GL_STENCIL_INDEX8 + + #ifndef MAGNUM_TARGET_GLES + , + + /** + * 16-bit stencil index. + * + * @requires_gl Use Format::%Stencil8 instead. + */ + Stencil16 = GL_STENCIL_INDEX1, + Depth24Stencil8 = GL_DEPTH24_STENCIL8, DepthFloatStencil8 = GL_DEPTH32F_STENCIL8 #endif - - /** @todo GL_STENCIL_INDEX1 - GL_STENCIL_INDEX16 (renderbuffer only) */ }; /** @copydoc AbstractTexture::InternalFormat */ diff --git a/src/Test/CMakeLists.txt b/src/Test/CMakeLists.txt index 61f80a3a6..6c20f9a69 100644 --- a/src/Test/CMakeLists.txt +++ b/src/Test/CMakeLists.txt @@ -1,3 +1,5 @@ corrade_add_test2(ObjectTest ObjectTest.cpp LIBRARIES MagnumTestLib) corrade_add_test2(CameraTest CameraTest.cpp LIBRARIES Magnum) corrade_add_test2(SceneTest SceneTest.cpp LIBRARIES Magnum) + +corrade_add_test2(ColorTest ColorTest.cpp) diff --git a/src/Test/ColorTest.cpp b/src/Test/ColorTest.cpp new file mode 100644 index 000000000..a500ffee4 --- /dev/null +++ b/src/Test/ColorTest.cpp @@ -0,0 +1,121 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "ColorTest.h" + +#include "Color.h" + +using namespace std; + +CORRADE_TEST_MAIN(Magnum::Test::ColorTest) + +namespace Magnum { namespace Test { + +typedef Magnum::Color3 Color3; +typedef Magnum::Color4 Color4; +typedef Magnum::Color3 Color3f; + +ColorTest::ColorTest() { + addTests(&ColorTest::fromDenormalized, + &ColorTest::fromNormalized, + + &ColorTest::fromHue, + &ColorTest::fromSaturation, + &ColorTest::fromValue, + + &ColorTest::hue, + &ColorTest::saturation, + &ColorTest::value, + + &ColorTest::hsv, + &ColorTest::hsvOverflow, + &ColorTest::hsvAlpha); +} + +void ColorTest::fromDenormalized() { + CORRADE_COMPARE(Color3f::fromDenormalized(Color3(75, 115, 224)), Color3f(0.294118, 0.45098, 0.878431)); +} + +void ColorTest::fromNormalized() { + CORRADE_COMPARE(Color3::fromNormalized(Color3f(0.294118, 0.45098, 0.878431)), Color3(75, 115, 224)); +} + +void ColorTest::fromHue() { + CORRADE_COMPARE(Color3::fromHSV(27.0f, 1.0f, 1.0f), Color3(255, 115, 0)); + CORRADE_COMPARE(Color3::fromHSV(86.0f, 1.0f, 1.0f), Color3(145, 255, 0)); + CORRADE_COMPARE(Color3::fromHSV(134.0f, 1.0f, 1.0f), Color3(0, 255, 60)); + CORRADE_COMPARE(Color3::fromHSV(191.0f, 1.0f, 1.0f), Color3(0, 208, 255)); + CORRADE_COMPARE(Color3::fromHSV(269.0f, 1.0f, 1.0f), Color3(123, 0, 255)); + CORRADE_COMPARE(Color3::fromHSV(317.0f, 1.0f, 1.0f), Color3(255, 0, 183)); +} + +void ColorTest::hue() { + CORRADE_COMPARE(Color3(255, 115, 0).hue(), 27.058824f); + CORRADE_COMPARE(Color3(145, 255, 0).hue(), 85.882353f); + CORRADE_COMPARE(Color3(0, 255, 60).hue(), 134.11765f); + CORRADE_COMPARE(Color3(0, 208, 255).hue(), 191.05882f); + CORRADE_COMPARE(Color3(123, 0, 255).hue(), 268.94117f); + CORRADE_COMPARE(Color3(255, 0, 183).hue(), 316.94117f); +} + +void ColorTest::fromSaturation() { + CORRADE_COMPARE(Color3::fromHSV(0.0f, 0.702f, 1.0f), Color3(255, 76, 76)); +} + +void ColorTest::saturation() { + CORRADE_COMPARE(Color3(255, 76, 76).saturation(), 0.701961f); + CORRADE_COMPARE(Color3().saturation(), 0.0f); +} + +void ColorTest::fromValue() { + CORRADE_COMPARE(Color3::fromHSV(0.0f, 1.0f, 0.522f), Color3(133, 0, 0)); +} + +void ColorTest::value() { + CORRADE_COMPARE(Color3(133, 0, 0).value(), 0.521569f); +} + +void ColorTest::hsv() { + CORRADE_COMPARE(Color3::fromHSV(230.0f, 0.749f, 0.427f), Color3(27, 41, 109)); + + float hue, saturation, value; + tie(hue, saturation, value) = Color3(27, 41, 109).toHSV(); + CORRADE_COMPARE(hue, 229.756106f); + CORRADE_COMPARE(saturation, 0.752294f); + CORRADE_COMPARE(value, 0.427451f); +} + +void ColorTest::hsvOverflow() { + CORRADE_COMPARE(Color3::fromHSV(27.0f-360.0f, 1.0f, 1.0f), Color3(255, 115, 0)); + CORRADE_COMPARE(Color3::fromHSV(86.0f-360.0f, 1.0f, 1.0f), Color3(145, 255, 0)); + CORRADE_COMPARE(Color3::fromHSV(134.0f-360.0f, 1.0f, 1.0f), Color3(0, 255, 60)); + CORRADE_COMPARE(Color3::fromHSV(191.0f-360.0f, 1.0f, 1.0f), Color3(0, 208, 255)); + CORRADE_COMPARE(Color3::fromHSV(269.0f-360.0f, 1.0f, 1.0f), Color3(123, 0, 255)); + CORRADE_COMPARE(Color3::fromHSV(317.0f-360.0f, 1.0f, 1.0f), Color3(255, 0, 183)); + + CORRADE_COMPARE(Color3::fromHSV(360.0f+27.0f, 1.0f, 1.0f), Color3(255, 115, 0)); + CORRADE_COMPARE(Color3::fromHSV(360.0f+86.0f, 1.0f, 1.0f), Color3(145, 255, 0)); + CORRADE_COMPARE(Color3::fromHSV(360.0f+134.0f, 1.0f, 1.0f), Color3(0, 255, 60)); + CORRADE_COMPARE(Color3::fromHSV(360.0f+191.0f, 1.0f, 1.0f), Color3(0, 208, 255)); + CORRADE_COMPARE(Color3::fromHSV(360.0f+269.0f, 1.0f, 1.0f), Color3(123, 0, 255)); + CORRADE_COMPARE(Color3::fromHSV(360.0f+317.0f, 1.0f, 1.0f), Color3(255, 0, 183)); +} + +void ColorTest::hsvAlpha() { + CORRADE_COMPARE(Color4::fromHSV(make_tuple(230.0f, 0.749f, 0.427f), 23), Color4(27, 41, 109, 23)); + CORRADE_COMPARE(Color4::fromHSV(230.0f, 0.749f, 0.427f, 23), Color4(27, 41, 109, 23)); +} + +}} diff --git a/src/Test/ColorTest.h b/src/Test/ColorTest.h new file mode 100644 index 000000000..068e36da6 --- /dev/null +++ b/src/Test/ColorTest.h @@ -0,0 +1,44 @@ +#ifndef Magnum_Test_ColorTest_h +#define Magnum_Test_ColorTest_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include + +namespace Magnum { namespace Test { + +class ColorTest: public Corrade::TestSuite::Tester { + public: + ColorTest(); + + void fromDenormalized(); + void fromNormalized(); + + void fromHue(); + void fromSaturation(); + void fromValue(); + + void hue(); + void saturation(); + void value(); + + void hsv(); + void hsvOverflow(); + void hsvAlpha(); +}; + +}} + +#endif