From 88515b2fca0baf99f89ccd39cfa5037d0f72234d Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Fri, 29 Apr 2022 09:37:29 -0600 Subject: [PATCH] Add polar coordinates constructor for `sf::Vector2` --- examples/tennis/Tennis.cpp | 3 +- include/SFML/System/Vector2.hpp | 22 +++++++++++-- include/SFML/System/Vector2.inl | 10 ++++++ src/SFML/Graphics/CircleShape.cpp | 6 +--- test/System/Vector2.cpp | 51 +++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 10 deletions(-) diff --git a/examples/tennis/Tennis.cpp b/examples/tennis/Tennis.cpp index 98c28cf0..149354f4 100644 --- a/examples/tennis/Tennis.cpp +++ b/examples/tennis/Tennis.cpp @@ -200,8 +200,7 @@ int main() } // Move the ball - float factor = ballSpeed * deltaTime; - ball.move({std::cos(ballAngle.asRadians()) * factor, std::sin(ballAngle.asRadians()) * factor}); + ball.move({ballSpeed * deltaTime, ballAngle}); #ifdef SFML_SYSTEM_IOS const std::string inputString = "Touch the screen to restart."; diff --git a/include/SFML/System/Vector2.hpp b/include/SFML/System/Vector2.hpp index 14dd6f2d..76b08a0e 100644 --- a/include/SFML/System/Vector2.hpp +++ b/include/SFML/System/Vector2.hpp @@ -52,7 +52,7 @@ public: constexpr Vector2(); //////////////////////////////////////////////////////////// - /// \brief Construct the vector from its coordinates + /// \brief Construct the vector from cartesian coordinates /// /// \param X X coordinate /// \param Y Y coordinate @@ -73,6 +73,22 @@ public: //////////////////////////////////////////////////////////// template constexpr explicit Vector2(const Vector2& vector); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from polar coordinates + /// + /// \param r Length of vector (can be negative) + /// \param phi Angle from X axis + /// + /// Note that this constructor is lossy: calling length() and angle() + /// may return values different to those provided in this constructor. + /// + /// In particular, these transforms can be applied: + /// * Vector2(r, phi) == Vector2(-r, phi + 180_deg) + /// * Vector2(r, phi) == Vector2(r, phi + n * 360_deg) + /// + //////////////////////////////////////////////////////////// + Vector2(T r, Angle phi); //////////////////////////////////////////////////////////// /// \brief Length of the vector (floating-point). @@ -103,7 +119,7 @@ public: /// /// \return The smallest angle which rotates \c *this in positive /// or negative direction, until it has the same direction as \c rhs. - /// The result has a sign and lies in the range [-180, 180°). + /// The result has a sign and lies in the range [-180, 180) degrees. /// \pre Neither \c *this nor \c rhs is a zero vector. /// //////////////////////////////////////////////////////////// @@ -114,7 +130,7 @@ public: /// /// For example, the vector (1,0) corresponds to 0 degrees, (0,1) corresponds to 90 degrees. /// - /// \return Angle in the range [-180°, 180°). + /// \return Angle in the range [-180, 180) degrees. /// \pre This vector is no zero vector. /// //////////////////////////////////////////////////////////// diff --git a/include/SFML/System/Vector2.inl b/include/SFML/System/Vector2.inl index 6ffaef97..1c6fdfd8 100644 --- a/include/SFML/System/Vector2.inl +++ b/include/SFML/System/Vector2.inl @@ -53,6 +53,16 @@ y(static_cast(vector.y)) } +//////////////////////////////////////////////////////////// +template +Vector2::Vector2(T r, Angle phi) : +x(r * static_cast(std::cos(phi.asRadians()))), +y(r * static_cast(std::sin(phi.asRadians()))) +{ + static_assert(std::is_floating_point_v, "Vector2::Vector2(T, Angle) is only supported for floating point types"); +} + + //////////////////////////////////////////////////////////// template T Vector2::length() const diff --git a/src/SFML/Graphics/CircleShape.cpp b/src/SFML/Graphics/CircleShape.cpp index 9d1c0596..d2ef144e 100644 --- a/src/SFML/Graphics/CircleShape.cpp +++ b/src/SFML/Graphics/CircleShape.cpp @@ -73,11 +73,7 @@ std::size_t CircleShape::getPointCount() const Vector2f CircleShape::getPoint(std::size_t index) const { Angle angle = static_cast(index) / static_cast(m_pointCount) * sf::degrees(360) - sf::degrees(90); - float rad = angle.asRadians(); - float x = std::cos(rad) * m_radius; - float y = std::sin(rad) * m_radius; - - return Vector2f(m_radius + x, m_radius + y); + return Vector2f(m_radius, m_radius) + Vector2f(m_radius, angle); } } // namespace sf diff --git a/test/System/Vector2.cpp b/test/System/Vector2.cpp index e586f15c..0cb1fd96 100644 --- a/test/System/Vector2.cpp +++ b/test/System/Vector2.cpp @@ -37,6 +37,57 @@ TEST_CASE("sf::Vector2 class template - [system]") CHECK(vector.x == static_cast(sourceVector.x)); CHECK(vector.y == static_cast(sourceVector.y)); } + + SUBCASE("Length and angle constructor") + { + CHECK(sf::Vector2f(0, sf::degrees(0)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(45)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(90)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(135)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(180)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(270)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(360)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(-90)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(-180)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(-270)) == sf::Vector2f(0, 0)); + CHECK(sf::Vector2f(0, sf::degrees(-360)) == sf::Vector2f(0, 0)); + + CHECK(sf::Vector2f(1, sf::degrees(0)) == sf::Vector2f(1, 0)); + CHECK(sf::Vector2f(1, sf::degrees(45)) == ApproxVec2(std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f)); + CHECK(sf::Vector2f(1, sf::degrees(90)) == ApproxVec2(0, 1)); + CHECK(sf::Vector2f(1, sf::degrees(135)) == ApproxVec2(-std::sqrt(2.f) / 2.f, std::sqrt(2.f) / 2.f)); + CHECK(sf::Vector2f(1, sf::degrees(180)) == ApproxVec2(-1, 0)); + CHECK(sf::Vector2f(1, sf::degrees(270)) == ApproxVec2(0, -1)); + CHECK(sf::Vector2f(1, sf::degrees(360)) == ApproxVec2(1, 0)); + CHECK(sf::Vector2f(1, sf::degrees(-90)) == ApproxVec2(0, -1)); + CHECK(sf::Vector2f(1, sf::degrees(-180)) == ApproxVec2(-1, 0)); + CHECK(sf::Vector2f(1, sf::degrees(-270)) == ApproxVec2(0, 1)); + CHECK(sf::Vector2f(1, sf::degrees(-360)) == ApproxVec2(1, 0)); + + CHECK(sf::Vector2f(-1, sf::degrees(0)) == sf::Vector2f(-1, 0)); + CHECK(sf::Vector2f(-1, sf::degrees(45)) == ApproxVec2(-std::sqrt(2.f) / 2.f, -std::sqrt(2.f) / 2.f)); + CHECK(sf::Vector2f(-1, sf::degrees(90)) == ApproxVec2(0, -1)); + CHECK(sf::Vector2f(-1, sf::degrees(135)) == ApproxVec2(std::sqrt(2.f) / 2.f, -std::sqrt(2.f) / 2.f)); + CHECK(sf::Vector2f(-1, sf::degrees(180)) == ApproxVec2(1, 0)); + CHECK(sf::Vector2f(-1, sf::degrees(270)) == ApproxVec2(0, 1)); + CHECK(sf::Vector2f(-1, sf::degrees(360)) == ApproxVec2(-1, 0)); + CHECK(sf::Vector2f(-1, sf::degrees(-90)) == ApproxVec2(0, 1)); + CHECK(sf::Vector2f(-1, sf::degrees(-180)) == ApproxVec2(1, 0)); + CHECK(sf::Vector2f(-1, sf::degrees(-270)) == ApproxVec2(0, -1)); + CHECK(sf::Vector2f(-1, sf::degrees(-360)) == ApproxVec2(-1, 0)); + + CHECK(sf::Vector2f(4.2f, sf::degrees(0)) == sf::Vector2f(4.2f, 0)); + CHECK(sf::Vector2f(4.2f, sf::degrees(45)) == ApproxVec2(4.2f * std::sqrt(2.f) / 2.f, 4.2f * std::sqrt(2.f) / 2.f)); + CHECK(sf::Vector2f(4.2f, sf::degrees(90)) == ApproxVec2(0, 4.2f)); + CHECK(sf::Vector2f(4.2f, sf::degrees(135)) == ApproxVec2(-4.2f * std::sqrt(2.f) / 2.f, 4.2f * std::sqrt(2.f) / 2.f)); + CHECK(sf::Vector2f(4.2f, sf::degrees(180)) == ApproxVec2(-4.2f, 0)); + CHECK(sf::Vector2f(4.2f, sf::degrees(270)) == ApproxVec2(0, -4.2f)); + CHECK(sf::Vector2f(4.2f, sf::degrees(360)) == ApproxVec2(4.2f, 0)); + CHECK(sf::Vector2f(4.2f, sf::degrees(-90)) == ApproxVec2(0, -4.2f)); + CHECK(sf::Vector2f(4.2f, sf::degrees(-180)) == ApproxVec2(-4.2f, 0)); + CHECK(sf::Vector2f(4.2f, sf::degrees(-270)) == ApproxVec2(0, 4.2f)); + CHECK(sf::Vector2f(4.2f, sf::degrees(-360)) == ApproxVec2(4.2f, 0)); + } } SUBCASE("Unary operations")