diff --git a/include/SFML/Graphics/Transform.hpp b/include/SFML/Graphics/Transform.hpp index a7db6f46..306887cd 100644 --- a/include/SFML/Graphics/Transform.hpp +++ b/include/SFML/Graphics/Transform.hpp @@ -31,16 +31,17 @@ #include #include #include -#include namespace sf { +class Angle; + //////////////////////////////////////////////////////////// /// \brief Define a 3x3 transform matrix /// //////////////////////////////////////////////////////////// -class SFML_GRAPHICS_API Transform +class Transform { public: @@ -50,7 +51,7 @@ public: /// Creates an identity transform (a transform that does nothing). /// //////////////////////////////////////////////////////////// - Transform(); + constexpr Transform(); //////////////////////////////////////////////////////////// /// \brief Construct a transform from a 3x3 matrix @@ -66,9 +67,9 @@ public: /// \param a22 Element (2, 2) of the matrix /// //////////////////////////////////////////////////////////// - Transform(float a00, float a01, float a02, - float a10, float a11, float a12, - float a20, float a21, float a22); + constexpr Transform(float a00, float a01, float a02, + float a10, float a11, float a12, + float a20, float a21, float a22); //////////////////////////////////////////////////////////// /// \brief Return the transform as a 4x4 matrix @@ -85,7 +86,7 @@ public: /// \return Pointer to a 4x4 matrix /// //////////////////////////////////////////////////////////// - const float* getMatrix() const; + constexpr const float* getMatrix() const; //////////////////////////////////////////////////////////// /// \brief Return the inverse of the transform @@ -96,7 +97,7 @@ public: /// \return A new transform which is the inverse of self /// //////////////////////////////////////////////////////////// - Transform getInverse() const; + constexpr Transform getInverse() const; //////////////////////////////////////////////////////////// /// \brief Transform a 2D point @@ -112,7 +113,7 @@ public: /// \return Transformed point /// //////////////////////////////////////////////////////////// - Vector2f transformPoint(const Vector2f& point) const; + constexpr Vector2f transformPoint(const Vector2f& point) const; //////////////////////////////////////////////////////////// /// \brief Transform a rectangle @@ -128,7 +129,7 @@ public: /// \return Transformed rectangle /// //////////////////////////////////////////////////////////// - FloatRect transformRect(const FloatRect& rectangle) const; + constexpr FloatRect transformRect(const FloatRect& rectangle) const; //////////////////////////////////////////////////////////// /// \brief Combine the current transform with another one @@ -148,7 +149,7 @@ public: /// \return Reference to *this /// //////////////////////////////////////////////////////////// - Transform& combine(const Transform& transform); + constexpr Transform& combine(const Transform& transform); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a translation @@ -167,7 +168,7 @@ public: /// \see rotate, scale /// //////////////////////////////////////////////////////////// - Transform& translate(const Vector2f& offset); + constexpr Transform& translate(const Vector2f& offset); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a rotation @@ -186,7 +187,7 @@ public: /// \see translate, scale /// //////////////////////////////////////////////////////////// - Transform& rotate(Angle angle); + SFML_GRAPHICS_API Transform& rotate(Angle angle); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a rotation @@ -211,7 +212,7 @@ public: /// \see translate, scale /// //////////////////////////////////////////////////////////// - Transform& rotate(Angle angle, const Vector2f& center); + SFML_GRAPHICS_API Transform& rotate(Angle angle, const Vector2f& center); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a scaling @@ -230,7 +231,7 @@ public: /// \see translate, rotate /// //////////////////////////////////////////////////////////// - Transform& scale(const Vector2f& factors); + constexpr Transform& scale(const Vector2f& factors); //////////////////////////////////////////////////////////// /// \brief Combine the current transform with a scaling @@ -255,7 +256,7 @@ public: /// \see translate, rotate /// //////////////////////////////////////////////////////////// - Transform& scale(const Vector2f& factors, const Vector2f& center); + constexpr Transform& scale(const Vector2f& factors, const Vector2f& center); //////////////////////////////////////////////////////////// // Static member data @@ -282,7 +283,7 @@ private: /// \return New combined transform /// //////////////////////////////////////////////////////////// -SFML_GRAPHICS_API Transform operator *(const Transform& left, const Transform& right); +constexpr Transform operator *(const Transform& left, const Transform& right); //////////////////////////////////////////////////////////// /// \relates sf::Transform @@ -296,7 +297,7 @@ SFML_GRAPHICS_API Transform operator *(const Transform& left, const Transform& r /// \return The combined transform /// //////////////////////////////////////////////////////////// -SFML_GRAPHICS_API Transform& operator *=(Transform& left, const Transform& right); +constexpr Transform& operator *=(Transform& left, const Transform& right); //////////////////////////////////////////////////////////// /// \relates sf::Transform @@ -310,7 +311,7 @@ SFML_GRAPHICS_API Transform& operator *=(Transform& left, const Transform& right /// \return New transformed point /// //////////////////////////////////////////////////////////// -SFML_GRAPHICS_API Vector2f operator *(const Transform& left, const Vector2f& right); +constexpr Vector2f operator *(const Transform& left, const Vector2f& right); //////////////////////////////////////////////////////////// /// \relates sf::Transform @@ -325,7 +326,7 @@ SFML_GRAPHICS_API Vector2f operator *(const Transform& left, const Vector2f& rig /// \return true if the transforms are equal, false otherwise /// //////////////////////////////////////////////////////////// -SFML_GRAPHICS_API bool operator ==(const Transform& left, const Transform& right); +[[nodiscard]] constexpr bool operator ==(const Transform& left, const Transform& right); //////////////////////////////////////////////////////////// /// \relates sf::Transform @@ -339,7 +340,9 @@ SFML_GRAPHICS_API bool operator ==(const Transform& left, const Transform& right /// \return true if the transforms are not equal, false otherwise /// //////////////////////////////////////////////////////////// -SFML_GRAPHICS_API bool operator !=(const Transform& left, const Transform& right); +[[nodiscard]] constexpr bool operator !=(const Transform& left, const Transform& right); + +#include } // namespace sf diff --git a/include/SFML/Graphics/Transform.inl b/include/SFML/Graphics/Transform.inl new file mode 100644 index 00000000..ecbf8225 --- /dev/null +++ b/include/SFML/Graphics/Transform.inl @@ -0,0 +1,222 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////// +constexpr Transform::Transform() + // Identity matrix + : m_matrix{1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f} +{ +} + + +//////////////////////////////////////////////////////////// +constexpr Transform::Transform(float a00, float a01, float a02, + float a10, float a11, float a12, + float a20, float a21, float a22) + : m_matrix{a00, a10, 0.f, a20, + a01, a11, 0.f, a21, + 0.f, 0.f, 1.f, 0.f, + a02, a12, 0.f, a22} +{ +} + + +//////////////////////////////////////////////////////////// +constexpr const float* Transform::getMatrix() const +{ + return m_matrix; +} + + +//////////////////////////////////////////////////////////// +constexpr Transform Transform::getInverse() const +{ + // Compute the determinant + float det = m_matrix[0] * (m_matrix[15] * m_matrix[5] - m_matrix[7] * m_matrix[13]) - + m_matrix[1] * (m_matrix[15] * m_matrix[4] - m_matrix[7] * m_matrix[12]) + + m_matrix[3] * (m_matrix[13] * m_matrix[4] - m_matrix[5] * m_matrix[12]); + + // Compute the inverse if the determinant is not zero + // (don't use an epsilon because the determinant may *really* be tiny) + if (det != 0.f) + { + return Transform( (m_matrix[15] * m_matrix[5] - m_matrix[7] * m_matrix[13]) / det, + -(m_matrix[15] * m_matrix[4] - m_matrix[7] * m_matrix[12]) / det, + (m_matrix[13] * m_matrix[4] - m_matrix[5] * m_matrix[12]) / det, + -(m_matrix[15] * m_matrix[1] - m_matrix[3] * m_matrix[13]) / det, + (m_matrix[15] * m_matrix[0] - m_matrix[3] * m_matrix[12]) / det, + -(m_matrix[13] * m_matrix[0] - m_matrix[1] * m_matrix[12]) / det, + (m_matrix[7] * m_matrix[1] - m_matrix[3] * m_matrix[5]) / det, + -(m_matrix[7] * m_matrix[0] - m_matrix[3] * m_matrix[4]) / det, + (m_matrix[5] * m_matrix[0] - m_matrix[1] * m_matrix[4]) / det); + } + else + { + return Identity; + } +} + + +//////////////////////////////////////////////////////////// +constexpr Vector2f Transform::transformPoint(const Vector2f& point) const +{ + return Vector2f(m_matrix[0] * point.x + m_matrix[4] * point.y + m_matrix[12], + m_matrix[1] * point.x + m_matrix[5] * point.y + m_matrix[13]); +} + + +//////////////////////////////////////////////////////////// +constexpr FloatRect Transform::transformRect(const FloatRect& rectangle) const +{ + // Transform the 4 corners of the rectangle + const Vector2f points[] = + { + transformPoint({rectangle.left, rectangle.top}), + transformPoint({rectangle.left, rectangle.top + rectangle.height}), + transformPoint({rectangle.left + rectangle.width, rectangle.top}), + transformPoint({rectangle.left + rectangle.width, rectangle.top + rectangle.height}) + }; + + // Compute the bounding rectangle of the transformed points + float left = points[0].x; + float top = points[0].y; + float right = points[0].x; + float bottom = points[0].y; + for (int i = 1; i < 4; ++i) + { + if (points[i].x < left) left = points[i].x; + else if (points[i].x > right) right = points[i].x; + if (points[i].y < top) top = points[i].y; + else if (points[i].y > bottom) bottom = points[i].y; + } + + return FloatRect({left, top}, {right - left, bottom - top}); +} + + +//////////////////////////////////////////////////////////// +constexpr Transform& Transform::combine(const Transform& transform) +{ + const float* a = m_matrix; + const float* b = transform.m_matrix; + + *this = Transform(a[0] * b[0] + a[4] * b[1] + a[12] * b[3], + a[0] * b[4] + a[4] * b[5] + a[12] * b[7], + a[0] * b[12] + a[4] * b[13] + a[12] * b[15], + a[1] * b[0] + a[5] * b[1] + a[13] * b[3], + a[1] * b[4] + a[5] * b[5] + a[13] * b[7], + a[1] * b[12] + a[5] * b[13] + a[13] * b[15], + a[3] * b[0] + a[7] * b[1] + a[15] * b[3], + a[3] * b[4] + a[7] * b[5] + a[15] * b[7], + a[3] * b[12] + a[7] * b[13] + a[15] * b[15]); + + return *this; +} + + +//////////////////////////////////////////////////////////// +constexpr Transform& Transform::translate(const Vector2f& offset) +{ + Transform translation(1, 0, offset.x, + 0, 1, offset.y, + 0, 0, 1); + + return combine(translation); +} + + +//////////////////////////////////////////////////////////// +constexpr Transform& Transform::scale(const Vector2f& factors) +{ + Transform scaling(factors.x, 0, 0, + 0, factors.y, 0, + 0, 0, 1); + + return combine(scaling); +} + + +//////////////////////////////////////////////////////////// +constexpr Transform& Transform::scale(const Vector2f& factors, const Vector2f& center) +{ + Transform scaling(factors.x, 0, center.x * (1 - factors.x), + 0, factors.y, center.y * (1 - factors.y), + 0, 0, 1); + + return combine(scaling); +} + + +//////////////////////////////////////////////////////////// +constexpr Transform operator *(const Transform& left, const Transform& right) +{ + return Transform(left).combine(right); +} + + +//////////////////////////////////////////////////////////// +constexpr Transform& operator *=(Transform& left, const Transform& right) +{ + return left.combine(right); +} + + +//////////////////////////////////////////////////////////// +constexpr Vector2f operator *(const Transform& left, const Vector2f& right) +{ + return left.transformPoint(right); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator ==(const Transform& left, const Transform& right) +{ + const float* a = left.getMatrix(); + const float* b = right.getMatrix(); + + return ((a[0] == b[0]) && (a[1] == b[1]) && (a[3] == b[3]) && + (a[4] == b[4]) && (a[5] == b[5]) && (a[7] == b[7]) && + (a[12] == b[12]) && (a[13] == b[13]) && (a[15] == b[15])); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator !=(const Transform& left, const Transform& right) +{ + return !(left == right); +} + + +//////////////////////////////////////////////////////////// +// Static member data +//////////////////////////////////////////////////////////// + +// Note: the 'inline' keyword here is technically not required, but VS2019 fails +// to compile with a bogus "multiple definition" error if not explicitly used. + +inline constexpr Transform Transform::Identity; diff --git a/include/SFML/Graphics/Transformable.hpp b/include/SFML/Graphics/Transformable.hpp index 381718de..edd1c4b9 100644 --- a/include/SFML/Graphics/Transformable.hpp +++ b/include/SFML/Graphics/Transformable.hpp @@ -30,6 +30,7 @@ //////////////////////////////////////////////////////////// #include #include +#include namespace sf diff --git a/include/SFML/Graphics/View.hpp b/include/SFML/Graphics/View.hpp index 10669680..be9dbdbe 100644 --- a/include/SFML/Graphics/View.hpp +++ b/include/SFML/Graphics/View.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include diff --git a/src/SFML/Graphics/CMakeLists.txt b/src/SFML/Graphics/CMakeLists.txt index ca2c5b24..2fd018da 100644 --- a/src/SFML/Graphics/CMakeLists.txt +++ b/src/SFML/Graphics/CMakeLists.txt @@ -42,6 +42,7 @@ set(SRC ${SRCROOT}/TextureSaver.hpp ${SRCROOT}/Transform.cpp ${INCROOT}/Transform.hpp + ${INCROOT}/Transform.inl ${SRCROOT}/Transformable.cpp ${INCROOT}/Transformable.hpp ${SRCROOT}/View.cpp diff --git a/src/SFML/Graphics/Transform.cpp b/src/SFML/Graphics/Transform.cpp index 804c916e..3e00d905 100644 --- a/src/SFML/Graphics/Transform.cpp +++ b/src/SFML/Graphics/Transform.cpp @@ -26,142 +26,12 @@ // Headers //////////////////////////////////////////////////////////// #include +#include #include namespace sf { -//////////////////////////////////////////////////////////// -const Transform Transform::Identity; - - -//////////////////////////////////////////////////////////// -Transform::Transform() -{ - // Identity matrix - m_matrix[0] = 1.f; m_matrix[4] = 0.f; m_matrix[8] = 0.f; m_matrix[12] = 0.f; - m_matrix[1] = 0.f; m_matrix[5] = 1.f; m_matrix[9] = 0.f; m_matrix[13] = 0.f; - m_matrix[2] = 0.f; m_matrix[6] = 0.f; m_matrix[10] = 1.f; m_matrix[14] = 0.f; - m_matrix[3] = 0.f; m_matrix[7] = 0.f; m_matrix[11] = 0.f; m_matrix[15] = 1.f; -} - - -//////////////////////////////////////////////////////////// -Transform::Transform(float a00, float a01, float a02, - float a10, float a11, float a12, - float a20, float a21, float a22) -{ - m_matrix[0] = a00; m_matrix[4] = a01; m_matrix[8] = 0.f; m_matrix[12] = a02; - m_matrix[1] = a10; m_matrix[5] = a11; m_matrix[9] = 0.f; m_matrix[13] = a12; - m_matrix[2] = 0.f; m_matrix[6] = 0.f; m_matrix[10] = 1.f; m_matrix[14] = 0.f; - m_matrix[3] = a20; m_matrix[7] = a21; m_matrix[11] = 0.f; m_matrix[15] = a22; -} - - -//////////////////////////////////////////////////////////// -const float* Transform::getMatrix() const -{ - return m_matrix; -} - - -//////////////////////////////////////////////////////////// -Transform Transform::getInverse() const -{ - // Compute the determinant - float det = m_matrix[0] * (m_matrix[15] * m_matrix[5] - m_matrix[7] * m_matrix[13]) - - m_matrix[1] * (m_matrix[15] * m_matrix[4] - m_matrix[7] * m_matrix[12]) + - m_matrix[3] * (m_matrix[13] * m_matrix[4] - m_matrix[5] * m_matrix[12]); - - // Compute the inverse if the determinant is not zero - // (don't use an epsilon because the determinant may *really* be tiny) - if (det != 0.f) - { - return Transform( (m_matrix[15] * m_matrix[5] - m_matrix[7] * m_matrix[13]) / det, - -(m_matrix[15] * m_matrix[4] - m_matrix[7] * m_matrix[12]) / det, - (m_matrix[13] * m_matrix[4] - m_matrix[5] * m_matrix[12]) / det, - -(m_matrix[15] * m_matrix[1] - m_matrix[3] * m_matrix[13]) / det, - (m_matrix[15] * m_matrix[0] - m_matrix[3] * m_matrix[12]) / det, - -(m_matrix[13] * m_matrix[0] - m_matrix[1] * m_matrix[12]) / det, - (m_matrix[7] * m_matrix[1] - m_matrix[3] * m_matrix[5]) / det, - -(m_matrix[7] * m_matrix[0] - m_matrix[3] * m_matrix[4]) / det, - (m_matrix[5] * m_matrix[0] - m_matrix[1] * m_matrix[4]) / det); - } - else - { - return Identity; - } -} - - -//////////////////////////////////////////////////////////// -Vector2f Transform::transformPoint(const Vector2f& point) const -{ - return Vector2f(m_matrix[0] * point.x + m_matrix[4] * point.y + m_matrix[12], - m_matrix[1] * point.x + m_matrix[5] * point.y + m_matrix[13]); -} - - -//////////////////////////////////////////////////////////// -FloatRect Transform::transformRect(const FloatRect& rectangle) const -{ - // Transform the 4 corners of the rectangle - const Vector2f points[] = - { - transformPoint({rectangle.left, rectangle.top}), - transformPoint({rectangle.left, rectangle.top + rectangle.height}), - transformPoint({rectangle.left + rectangle.width, rectangle.top}), - transformPoint({rectangle.left + rectangle.width, rectangle.top + rectangle.height}) - }; - - // Compute the bounding rectangle of the transformed points - float left = points[0].x; - float top = points[0].y; - float right = points[0].x; - float bottom = points[0].y; - for (int i = 1; i < 4; ++i) - { - if (points[i].x < left) left = points[i].x; - else if (points[i].x > right) right = points[i].x; - if (points[i].y < top) top = points[i].y; - else if (points[i].y > bottom) bottom = points[i].y; - } - - return FloatRect({left, top}, {right - left, bottom - top}); -} - - -//////////////////////////////////////////////////////////// -Transform& Transform::combine(const Transform& transform) -{ - const float* a = m_matrix; - const float* b = transform.m_matrix; - - *this = Transform(a[0] * b[0] + a[4] * b[1] + a[12] * b[3], - a[0] * b[4] + a[4] * b[5] + a[12] * b[7], - a[0] * b[12] + a[4] * b[13] + a[12] * b[15], - a[1] * b[0] + a[5] * b[1] + a[13] * b[3], - a[1] * b[4] + a[5] * b[5] + a[13] * b[7], - a[1] * b[12] + a[5] * b[13] + a[13] * b[15], - a[3] * b[0] + a[7] * b[1] + a[15] * b[3], - a[3] * b[4] + a[7] * b[5] + a[15] * b[7], - a[3] * b[12] + a[7] * b[13] + a[15] * b[15]); - - return *this; -} - - -//////////////////////////////////////////////////////////// -Transform& Transform::translate(const Vector2f& offset) -{ - Transform translation(1, 0, offset.x, - 0, 1, offset.y, - 0, 0, 1); - - return combine(translation); -} - - //////////////////////////////////////////////////////////// Transform& Transform::rotate(Angle angle) { @@ -191,66 +61,4 @@ Transform& Transform::rotate(Angle angle, const Vector2f& center) return combine(rotation); } - -//////////////////////////////////////////////////////////// -Transform& Transform::scale(const Vector2f& factors) -{ - Transform scaling(factors.x, 0, 0, - 0, factors.y, 0, - 0, 0, 1); - - return combine(scaling); -} - - -//////////////////////////////////////////////////////////// -Transform& Transform::scale(const Vector2f& factors, const Vector2f& center) -{ - Transform scaling(factors.x, 0, center.x * (1 - factors.x), - 0, factors.y, center.y * (1 - factors.y), - 0, 0, 1); - - return combine(scaling); -} - - -//////////////////////////////////////////////////////////// -Transform operator *(const Transform& left, const Transform& right) -{ - return Transform(left).combine(right); -} - - -//////////////////////////////////////////////////////////// -Transform& operator *=(Transform& left, const Transform& right) -{ - return left.combine(right); -} - - -//////////////////////////////////////////////////////////// -Vector2f operator *(const Transform& left, const Vector2f& right) -{ - return left.transformPoint(right); -} - - -//////////////////////////////////////////////////////////// -bool operator ==(const Transform& left, const Transform& right) -{ - const float* a = left.getMatrix(); - const float* b = right.getMatrix(); - - return ((a[0] == b[0]) && (a[1] == b[1]) && (a[3] == b[3]) && - (a[4] == b[4]) && (a[5] == b[5]) && (a[7] == b[7]) && - (a[12] == b[12]) && (a[13] == b[13]) && (a[15] == b[15])); -} - - -//////////////////////////////////////////////////////////// -bool operator !=(const Transform& left, const Transform& right) -{ - return !(left == right); -} - } // namespace sf diff --git a/test/Graphics/Transform.cpp b/test/Graphics/Transform.cpp index e785ef45..c91d202f 100644 --- a/test/Graphics/Transform.cpp +++ b/test/Graphics/Transform.cpp @@ -1,4 +1,5 @@ #include +#include #include "GraphicsUtil.hpp" #include "SystemUtil.hpp" #include