Add sf::Angle

Similar to sf::Time, sf::Angle provides a typesafe API for working
with angles and provides named functions for converting to and from
degrees and radians.
This commit is contained in:
Chris Thrasher 2022-01-22 18:11:08 -07:00 committed by Vittorio Romeo
parent ef8d6ee7f7
commit 28f273b9c9
24 changed files with 1223 additions and 93 deletions

View File

@ -47,8 +47,7 @@
// Setup a perspective projection // Setup a perspective projection
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
static const float pi = 3.141592654f; float extent = std::tan(sf::degrees(45).asRadians());
float extent = std::tan(90.0f * pi / 360.0f);
#ifdef SFML_OPENGL_ES #ifdef SFML_OPENGL_ES
glFrustumf(-extent, extent, -extent, extent, 1.0f, 500.0f); glFrustumf(-extent, extent, -extent, extent, 1.0f, 500.0f);

View File

@ -308,7 +308,7 @@ public:
// Move to the center of the window // Move to the center of the window
m_transform.translate({400.f, 300.f}); m_transform.translate({400.f, 300.f});
// Rotate everything based on cursor position // Rotate everything based on cursor position
m_transform.rotate(x * 360.f); m_transform.rotate(sf::degrees(x * 360.f));
// Adjust billboard size to scale between 25 and 75 // Adjust billboard size to scale between 25 and 75
float size = 25 + std::abs(y) * 50; float size = 25 + std::abs(y) * 50;

View File

@ -32,7 +32,6 @@ int main()
std::srand(static_cast<unsigned int>(std::time(nullptr))); std::srand(static_cast<unsigned int>(std::time(nullptr)));
// Define some constants // Define some constants
const float pi = 3.14159f;
const float gameWidth = 800; const float gameWidth = 800;
const float gameHeight = 600; const float gameHeight = 600;
sf::Vector2f paddleSize(25, 100); sf::Vector2f paddleSize(25, 100);
@ -105,7 +104,7 @@ int main()
const float paddleSpeed = 400.f; const float paddleSpeed = 400.f;
float rightPaddleSpeed = 0.f; float rightPaddleSpeed = 0.f;
const float ballSpeed = 400.f; const float ballSpeed = 400.f;
float ballAngle = 0.f; // to be changed later sf::Angle ballAngle = sf::degrees(0); // to be changed later
sf::Clock clock; sf::Clock clock;
bool isPlaying = false; bool isPlaying = false;
@ -142,9 +141,9 @@ int main()
do do
{ {
// Make sure the ball initial angle is not too much vertical // Make sure the ball initial angle is not too much vertical
ballAngle = static_cast<float>(std::rand() % 360) * 2.f * pi / 360.f; ballAngle = sf::degrees(static_cast<float>(std::rand() % 360));
} }
while (std::abs(std::cos(ballAngle)) < 0.7f); while (std::abs(std::cos(ballAngle.asRadians())) < 0.7f);
} }
} }
@ -202,7 +201,7 @@ int main()
// Move the ball // Move the ball
float factor = ballSpeed * deltaTime; float factor = ballSpeed * deltaTime;
ball.move({std::cos(ballAngle) * factor, std::sin(ballAngle) * factor}); ball.move({std::cos(ballAngle.asRadians()) * factor, std::sin(ballAngle.asRadians()) * factor});
#ifdef SFML_SYSTEM_IOS #ifdef SFML_SYSTEM_IOS
const std::string inputString = "Touch the screen to restart."; const std::string inputString = "Touch the screen to restart.";
@ -242,9 +241,9 @@ int main()
ball.getPosition().y - ballRadius <= leftPaddle.getPosition().y + paddleSize.y / 2) ball.getPosition().y - ballRadius <= leftPaddle.getPosition().y + paddleSize.y / 2)
{ {
if (ball.getPosition().y > leftPaddle.getPosition().y) if (ball.getPosition().y > leftPaddle.getPosition().y)
ballAngle = pi - ballAngle + static_cast<float>(std::rand() % 20) * pi / 180; ballAngle = sf::degrees(180) - ballAngle + sf::degrees(static_cast<float>(std::rand() % 20));
else else
ballAngle = pi - ballAngle - static_cast<float>(std::rand() % 20) * pi / 180; ballAngle = sf::degrees(180) - ballAngle - sf::degrees(static_cast<float>(std::rand() % 20));
ballSound.play(); ballSound.play();
ball.setPosition({leftPaddle.getPosition().x + ballRadius + paddleSize.x / 2 + 0.1f, ball.getPosition().y}); ball.setPosition({leftPaddle.getPosition().x + ballRadius + paddleSize.x / 2 + 0.1f, ball.getPosition().y});
@ -257,9 +256,9 @@ int main()
ball.getPosition().y - ballRadius <= rightPaddle.getPosition().y + paddleSize.y / 2) ball.getPosition().y - ballRadius <= rightPaddle.getPosition().y + paddleSize.y / 2)
{ {
if (ball.getPosition().y > rightPaddle.getPosition().y) if (ball.getPosition().y > rightPaddle.getPosition().y)
ballAngle = pi - ballAngle + static_cast<float>(std::rand() % 20) * pi / 180; ballAngle = sf::degrees(180) - ballAngle + sf::degrees(static_cast<float>(std::rand() % 20));
else else
ballAngle = pi - ballAngle - static_cast<float>(std::rand() % 20) * pi / 180; ballAngle = sf::degrees(180) - ballAngle - sf::degrees(static_cast<float>(std::rand() % 20));
ballSound.play(); ballSound.play();
ball.setPosition({rightPaddle.getPosition().x - ballRadius - paddleSize.x / 2 - 0.1f, ball.getPosition().y}); ball.setPosition({rightPaddle.getPosition().x - ballRadius - paddleSize.x / 2 - 0.1f, ball.getPosition().y});

View File

@ -39,39 +39,42 @@ namespace
} }
// Rotate a matrix around the x-axis // Rotate a matrix around the x-axis
void matrixRotateX(Matrix& result, float angle) void matrixRotateX(Matrix& result, sf::Angle angle)
{ {
float rad = angle.asRadians();
Matrix matrix = { Matrix matrix = {
{1.f, 0.f, 0.f, 0.f}, {1.f, 0.f, 0.f, 0.f},
{0.f, std::cos(angle), std::sin(angle), 0.f}, {0.f, std::cos(rad), std::sin(rad), 0.f},
{0.f, -std::sin(angle), std::cos(angle), 0.f}, {0.f, -std::sin(rad), std::cos(rad), 0.f},
{0.f, 0.f, 0.f, 1.f} {0.f, 0.f, 0.f, 1.f}
}; };
matrixMultiply(result, result, matrix); matrixMultiply(result, result, matrix);
} }
// Rotate a matrix around the y-axis // Rotate a matrix around the y-axis
void matrixRotateY(Matrix& result, float angle) void matrixRotateY(Matrix& result, sf::Angle angle)
{ {
float rad = angle.asRadians();
Matrix matrix = { Matrix matrix = {
{ std::cos(angle), 0.f, std::sin(angle), 0.f}, { std::cos(rad), 0.f, std::sin(rad), 0.f},
{ 0.f, 1.f, 0.f, 0.f}, { 0.f, 1.f, 0.f, 0.f},
{-std::sin(angle), 0.f, std::cos(angle), 0.f}, {-std::sin(rad), 0.f, std::cos(rad), 0.f},
{ 0.f, 0.f, 0.f, 1.f} { 0.f, 0.f, 0.f, 1.f}
}; };
matrixMultiply(result, result, matrix); matrixMultiply(result, result, matrix);
} }
// Rotate a matrix around the z-axis // Rotate a matrix around the z-axis
void matrixRotateZ(Matrix& result, float angle) void matrixRotateZ(Matrix& result, sf::Angle angle)
{ {
float rad = angle.asRadians();
Matrix matrix = { Matrix matrix = {
{ std::cos(angle), std::sin(angle), 0.f, 0.f}, { std::cos(rad), std::sin(rad), 0.f, 0.f},
{-std::sin(angle), std::cos(angle), 0.f, 0.f}, {-std::sin(rad), std::cos(rad), 0.f, 0.f},
{ 0.f, 0.f, 1.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, 1.f}
}; };
matrixMultiply(result, result, matrix); matrixMultiply(result, result, matrix);
@ -128,9 +131,9 @@ namespace
} }
// Construct a perspective projection matrix // Construct a perspective projection matrix
void matrixPerspective(Matrix& result, float fov, float aspect, float nearPlane, float farPlane) void matrixPerspective(Matrix& result, sf::Angle fov, float aspect, float nearPlane, float farPlane)
{ {
const float a = 1.f / std::tan(fov / 2.f); const float a = 1.f / std::tan(fov.asRadians() / 2.f);
result[0][0] = a / aspect; result[0][0] = a / aspect;
result[0][1] = 0.f; result[0][1] = 0.f;
@ -2316,8 +2319,6 @@ public:
// Update the matrices in our uniform buffer every frame // Update the matrices in our uniform buffer every frame
void updateUniformBuffer(float elapsed) void updateUniformBuffer(float elapsed)
{ {
const float pi = 3.14159265359f;
// Construct the model matrix // Construct the model matrix
Matrix model = { Matrix model = {
{ 1.0f, 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 0.0f },
@ -2326,9 +2327,9 @@ public:
{ 0.0f, 0.0f, 0.0f, 1.0f } { 0.0f, 0.0f, 0.0f, 1.0f }
}; };
matrixRotateX(model, elapsed * 59.0f * pi / 180.f); matrixRotateX(model, sf::degrees(elapsed * 59.0f));
matrixRotateY(model, elapsed * 83.0f * pi / 180.f); matrixRotateY(model, sf::degrees(elapsed * 83.0f));
matrixRotateZ(model, elapsed * 109.0f * pi / 180.f); matrixRotateZ(model, sf::degrees(elapsed * 109.0f));
// Translate the model based on the mouse position // Translate the model based on the mouse position
sf::Vector2f mousePosition = sf::Vector2f(sf::Mouse::getPosition(window)); sf::Vector2f mousePosition = sf::Vector2f(sf::Mouse::getPosition(window));
@ -2349,14 +2350,14 @@ public:
matrixLookAt(view, eye, center, up); matrixLookAt(view, eye, center, up);
// Construct the projection matrix // Construct the projection matrix
const float fov = 45.0f; const sf::Angle fov = sf::degrees(45);
const float aspect = static_cast<float>(swapchainExtent.width) / static_cast<float>(swapchainExtent.height); const float aspect = static_cast<float>(swapchainExtent.width) / static_cast<float>(swapchainExtent.height);
const float nearPlane = 0.1f; const float nearPlane = 0.1f;
const float farPlane = 10.0f; const float farPlane = 10.0f;
Matrix projection; Matrix projection;
matrixPerspective(projection, fov * pi / 180.f, aspect, nearPlane, farPlane); matrixPerspective(projection, fov, aspect, nearPlane, farPlane);
char* ptr; char* ptr;

View File

@ -112,7 +112,7 @@ int main()
SFMLView2.clear(); SFMLView2.clear();
// Draw sprite 1 on view 1 // Draw sprite 1 on view 1
sprite1.setRotation(time * 100); sprite1.setRotation(sf::degrees(time * 100));
SFMLView1.draw(sprite1); SFMLView1.draw(sprite1);
// Draw sprite 2 on view 2 // Draw sprite 2 on view 2

View File

@ -31,6 +31,7 @@
#include <SFML/Graphics/Export.hpp> #include <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/Rect.hpp> #include <SFML/Graphics/Rect.hpp>
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <SFML/System/Angle.hpp>
namespace sf namespace sf
@ -156,7 +157,7 @@ public:
/// can be chained. /// can be chained.
/// \code /// \code
/// sf::Transform transform; /// sf::Transform transform;
/// transform.translate(sf::Vector2f(100, 200)).rotate(45); /// transform.translate(sf::Vector2f(100, 200)).rotate(sf::degrees(45));
/// \endcode /// \endcode
/// ///
/// \param offset Translation offset to apply /// \param offset Translation offset to apply
@ -175,17 +176,17 @@ public:
/// can be chained. /// can be chained.
/// \code /// \code
/// sf::Transform transform; /// sf::Transform transform;
/// transform.rotate(90).translate(50, 20); /// transform.rotate(sf::degrees(90)).translate(50, 20);
/// \endcode /// \endcode
/// ///
/// \param angle Rotation angle, in degrees /// \param angle Rotation angle
/// ///
/// \return Reference to *this /// \return Reference to *this
/// ///
/// \see translate, scale /// \see translate, scale
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Transform& rotate(float angle); Transform& rotate(Angle angle);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Combine the current transform with a rotation /// \brief Combine the current transform with a rotation
@ -199,10 +200,10 @@ public:
/// can be chained. /// can be chained.
/// \code /// \code
/// sf::Transform transform; /// sf::Transform transform;
/// transform.rotate(90, sf::Vector2f(8, 3)).translate(sf::Vector2f(50, 20)); /// transform.rotate(sf::degrees(90), sf::Vector2f(8, 3)).translate(sf::Vector2f(50, 20));
/// \endcode /// \endcode
/// ///
/// \param angle Rotation angle, in degrees /// \param angle Rotation angle
/// \param center Center of rotation /// \param center Center of rotation
/// ///
/// \return Reference to *this /// \return Reference to *this
@ -210,7 +211,7 @@ public:
/// \see translate, scale /// \see translate, scale
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Transform& rotate(float angle, const Vector2f& center); Transform& rotate(Angle angle, const Vector2f& center);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Combine the current transform with a scaling /// \brief Combine the current transform with a scaling
@ -219,7 +220,7 @@ public:
/// can be chained. /// can be chained.
/// \code /// \code
/// sf::Transform transform; /// sf::Transform transform;
/// transform.scale(sf::Vector2f(2, 1)).rotate(45); /// transform.scale(sf::Vector2f(2, 1)).rotate(sf::degrees(45));
/// \endcode /// \endcode
/// ///
/// \param factors Scaling factors /// \param factors Scaling factors

View File

@ -75,12 +75,12 @@ public:
/// See the rotate function to add an angle based on the previous rotation instead. /// See the rotate function to add an angle based on the previous rotation instead.
/// The default rotation of a transformable object is 0. /// The default rotation of a transformable object is 0.
/// ///
/// \param angle New rotation, in degrees /// \param angle New rotation
/// ///
/// \see rotate, getRotation /// \see rotate, getRotation
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void setRotation(float angle); void setRotation(Angle angle);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief set the scale factors of the object /// \brief set the scale factors of the object
@ -128,12 +128,12 @@ public:
/// ///
/// The rotation is always in the range [0, 360]. /// The rotation is always in the range [0, 360].
/// ///
/// \return Current rotation, in degrees /// \return Current rotation
/// ///
/// \see setRotation /// \see setRotation
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
float getRotation() const; Angle getRotation() const;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief get the current scale of the object /// \brief get the current scale of the object
@ -182,10 +182,10 @@ public:
/// object.setRotation(object.getRotation() + angle); /// object.setRotation(object.getRotation() + angle);
/// \endcode /// \endcode
/// ///
/// \param angle Angle of rotation, in degrees /// \param angle Angle of rotation
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void rotate(float angle); void rotate(Angle angle);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Scale the object /// \brief Scale the object
@ -232,7 +232,7 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Vector2f m_origin; //!< Origin of translation/rotation/scaling of the object Vector2f m_origin; //!< Origin of translation/rotation/scaling of the object
Vector2f m_position; //!< Position of the object in the 2D world Vector2f m_position; //!< Position of the object in the 2D world
float m_rotation; //!< Orientation of the object, in degrees Angle m_rotation; //!< Orientation of the object
Vector2f m_scale; //!< Scale of the object Vector2f m_scale; //!< Scale of the object
mutable Transform m_transform; //!< Combined transformation of the object mutable Transform m_transform; //!< Combined transformation of the object
mutable bool m_transformNeedUpdate; //!< Does the transform need to be recomputed? mutable bool m_transformNeedUpdate; //!< Does the transform need to be recomputed?

View File

@ -105,12 +105,12 @@ public:
/// ///
/// The default rotation of a view is 0 degree. /// The default rotation of a view is 0 degree.
/// ///
/// \param angle New angle, in degrees /// \param angle New angle
/// ///
/// \see getRotation /// \see getRotation
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void setRotation(float angle); void setRotation(Angle angle);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Set the target viewport /// \brief Set the target viewport
@ -164,12 +164,12 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the current orientation of the view /// \brief Get the current orientation of the view
/// ///
/// \return Rotation angle of the view, in degrees /// \return Rotation angle of the view
/// ///
/// \see setRotation /// \see setRotation
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
float getRotation() const; Angle getRotation() const;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the target viewport rectangle of the view /// \brief Get the target viewport rectangle of the view
@ -205,12 +205,12 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Rotate the view relatively to its current orientation /// \brief Rotate the view relatively to its current orientation
/// ///
/// \param angle Angle to rotate, in degrees /// \param angle Angle to rotate
/// ///
/// \see setRotation, move, zoom /// \see setRotation, move, zoom
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void rotate(float angle); void rotate(Angle angle);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Resize the view rectangle relatively to its current size /// \brief Resize the view rectangle relatively to its current size
@ -260,7 +260,7 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Vector2f m_center; //!< Center of the view, in scene coordinates Vector2f m_center; //!< Center of the view, in scene coordinates
Vector2f m_size; //!< Size of the view, in scene coordinates Vector2f m_size; //!< Size of the view, in scene coordinates
float m_rotation; //!< Angle of rotation of the view rectangle, in degrees Angle m_rotation; //!< Angle of rotation of the view rectangle
FloatRect m_viewport; //!< Viewport rectangle, expressed as a factor of the render-target's size FloatRect m_viewport; //!< Viewport rectangle, expressed as a factor of the render-target's size
mutable Transform m_transform; //!< Precomputed projection transform corresponding to the view mutable Transform m_transform; //!< Precomputed projection transform corresponding to the view
mutable Transform m_inverseTransform; //!< Precomputed inverse projection transform corresponding to the view mutable Transform m_inverseTransform; //!< Precomputed inverse projection transform corresponding to the view
@ -307,7 +307,7 @@ private:
/// view.reset(sf::FloatRect(100, 100, 400, 200)); /// view.reset(sf::FloatRect(100, 100, 400, 200));
/// ///
/// // Rotate it by 45 degrees /// // Rotate it by 45 degrees
/// view.rotate(45); /// view.rotate(sf::degrees(45));
/// ///
/// // Set its target viewport to be half of the window /// // Set its target viewport to be half of the window
/// view.setViewport(sf::FloatRect(0.f, 0.f, 0.5f, 1.f)); /// view.setViewport(sf::FloatRect(0.f, 0.f, 0.5f, 1.f));

View File

@ -30,6 +30,7 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/Config.hpp> #include <SFML/Config.hpp>
#include <SFML/System/Angle.hpp>
#include <SFML/System/Clock.hpp> #include <SFML/System/Clock.hpp>
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#include <SFML/System/FileInputStream.hpp> #include <SFML/System/FileInputStream.hpp>

View File

@ -0,0 +1,519 @@
////////////////////////////////////////////////////////////
//
// 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.
//
////////////////////////////////////////////////////////////
#ifndef SFML_ANGLE_HPP
#define SFML_ANGLE_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Export.hpp>
#include <cassert>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Represents an angle value.
///
////////////////////////////////////////////////////////////
class Angle
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Sets the angle value to zero.
///
////////////////////////////////////////////////////////////
constexpr Angle();
////////////////////////////////////////////////////////////
/// \brief Return the angle's value in degrees
///
/// \return Angle in degrees
///
/// \see asRadians
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr float asDegrees() const;
////////////////////////////////////////////////////////////
/// \brief Return the angle's value in radians
///
/// \return Angle in radians
///
/// \see asDegrees
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr float asRadians() const;
////////////////////////////////////////////////////////////
/// \brief Wrap to a range such that -180° <= angle < 180°
///
/// Similar to a modulo operation, this returns a copy of the angle
/// constrained to the range [-180°, 180°) == [-Pi, Pi).
/// The resulting angle represents a rotation which is equivalent to *this.
///
/// The name "signed" originates from the similarity to signed integers:
/// <table>
/// <tr>
/// <th></th>
/// <th>signed</th>
/// <th>unsigned</th>
/// </tr>
/// <tr>
/// <td>char</td>
/// <td>[-128, 128)</td>
/// <td>[0, 256)</td>
/// </tr>
/// <tr>
/// <td>Angle</td>
/// <td>[-180°, 180°)</td>
/// <td>[0°, 360°)</td>
/// </tr>
/// </table>
///
/// \return Signed angle, wrapped to [-180°, 180°)
///
/// \see wrapUnsigned
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle wrapSigned() const;
////////////////////////////////////////////////////////////
/// \brief Wrap to a range such that 0° <= angle < 360°
///
/// Similar to a modulo operation, this returns a copy of the angle
/// constrained to the range [0°, 360°) == [0, Tau) == [0, 2*Pi).
/// The resulting angle represents a rotation which is equivalent to *this.
///
/// The name "unsigned" originates from the similarity to unsigned integers:
/// <table>
/// <tr>
/// <th></th>
/// <th>signed</th>
/// <th>unsigned</th>
/// </tr>
/// <tr>
/// <td>char</td>
/// <td>[-128, 128)</td>
/// <td>[0, 256)</td>
/// </tr>
/// <tr>
/// <td>Angle</td>
/// <td>[-180°, 180°)</td>
/// <td>[0°, 360°)</td>
/// </tr>
/// </table>
///
/// \return Unsigned angle, wrapped to [0°, 360°)
///
/// \see wrapSigned
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle wrapUnsigned() const;
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
SFML_SYSTEM_API static const Angle Zero; //!< Predefined 0 degree angle value
private:
friend constexpr Angle degrees(float angle);
friend constexpr Angle radians(float angle);
////////////////////////////////////////////////////////////
/// \brief Construct from a number of degrees
///
/// This function is internal. To construct angle values,
/// use sf::radians or sf::degrees instead.
///
/// \param degrees Angle in degrees
///
////////////////////////////////////////////////////////////
constexpr explicit Angle(float degrees);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
float m_degrees; //!< Angle value stored as degrees
};
////////////////////////////////////////////////////////////
/// \brief Construct an angle value from a number of degrees
///
/// \param angle Number of degrees
///
/// \return Angle value constructed from the number of degrees
///
/// \see radians
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle degrees(float angle);
////////////////////////////////////////////////////////////
/// \brief Construct an angle value from a number of radians
///
/// \param angle Number of radians
///
/// \return Angle value constructed from the number of radians
///
/// \see degrees
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle radians(float angle);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of == operator to compare two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return True if both angle values are equal
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator ==(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of != operator to compare two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return True if both angle values are different
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator !=(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of < operator to compare two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return True if \a left is less than \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator <(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of > operator to compare two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return True if \a left is greater than \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator >(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of <= operator to compare two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return True if \a left is less than or equal to \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator <=(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of >= operator to compare two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return True if \a left is greater than or equal to \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr bool operator >=(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of unary - operator to negate an angle value.
///
/// Represents a rotation in the opposite direction.
///
/// \param right Right operand (an angle)
///
/// \return Negative of the angle value
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator -(Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary + operator to add two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return Sum of the two angle values
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator +(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary += operator to add/assign two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return Sum of the two angle values
///
////////////////////////////////////////////////////////////
constexpr Angle& operator +=(Angle& left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary - operator to subtract two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return Difference of the two angle values
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator -(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary -= operator to subtract/assign two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return Difference of the two angle values
///
////////////////////////////////////////////////////////////
constexpr Angle& operator -=(Angle& left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary * operator to scale an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (a number)
///
/// \return \a left multiplied by \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator *(Angle left, float right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary * operator to scale an angle value
///
/// \param left Left operand (a number)
/// \param right Right operand (an angle)
///
/// \return \a left multiplied by \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator *(float left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary *= operator to scale/assign an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (a number)
///
/// \return \a left multiplied by \a right
///
////////////////////////////////////////////////////////////
constexpr Angle& operator *=(Angle& left, float right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary / operator to scale an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (a number)
///
/// \return \a left divided by \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator /(Angle left, float right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary /= operator to scale/assign an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (a number)
///
/// \return \a left divided by \a right
///
////////////////////////////////////////////////////////////
constexpr Angle& operator /=(Angle& left, float right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary / operator to compute the ratio of two angle values
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return \a left divided by \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr float operator /(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary % operator to compute modulo of an angle value.
///
/// Right hand angle must be greater than zero.
///
/// Examples:
/// \code
/// sf::degrees(90) % sf::degrees(40) // 10 degrees
/// sf::degrees(-90) % sf::degrees(40) // 30 degrees (not -10)
/// \endcode
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return \a left modulo \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator %(Angle left, Angle right);
////////////////////////////////////////////////////////////
/// \relates Angle
/// \brief Overload of binary %= operator to compute/assign remainder of an angle value
///
/// \param left Left operand (an angle)
/// \param right Right operand (an angle)
///
/// \return \a left modulo \a right
///
////////////////////////////////////////////////////////////
constexpr Angle& operator %=(Angle& left, Angle right);
namespace Literals
{
////////////////////////////////////////////////////////////
/// \relates sf::Angle
/// \brief User defined literal for angles in degrees, e.g. 10.5_deg
///
/// \param angle Angle in degrees
///
/// \return \a Angle
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator "" _deg(long double angle);
////////////////////////////////////////////////////////////
/// \relates sf::Angle
/// \brief User defined literal for angles in degrees, e.g. 90_deg
///
/// \param angle Angle in degrees
///
/// \return \a Angle
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator "" _deg(unsigned long long int angle);
////////////////////////////////////////////////////////////
/// \relates sf::Angle
/// \brief User defined literal for angles in radians, e.g. 0.1_rad
///
/// \param angle Angle in radians
///
/// \return \a Angle
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator "" _rad(long double angle);
////////////////////////////////////////////////////////////
/// \relates sf::Angle
/// \brief User defined literal for angles in radians, e.g. 2_rad
///
/// \param angle Angle in radians
///
/// \return \a Angle
///
////////////////////////////////////////////////////////////
[[nodiscard]] constexpr Angle operator "" _rad(unsigned long long int angle);
} // namespace Literals
#include <SFML/System/Angle.inl>
} // namespace sf
#endif // SFML_ANGLE_HPP
////////////////////////////////////////////////////////////
/// \class sf::Angle
/// \ingroup system
///
/// sf::Angle encapsulates an angle value in a flexible way.
/// It allows for defining an angle value either as a number
/// of degrees or radians. It also works the other way
/// around. You can read an angle value as either a number
/// of degrees or radians.
///
/// By using such a flexible interface, the API doesn't
/// impose any fixed type or unit for angle values and lets
/// the user choose their own preferred representation.
///
/// Angle values support the usual mathematical operations.
/// You can add or subtract two angles, multiply or divide
/// an angle by a number, compare two angles, etc.
///
/// Usage example:
/// \code
/// sf::Angle a1 = sf::degrees(90);
/// float radians = a1.asRadians(); // 1.5708f
///
/// sf::Angle a2 = sf::radians(3.141592654f);
/// float degrees = a2.asDegrees(); // 180.0f
///
/// using namespace sf::Literals;
/// sf::Angle a3 = 10_deg; // 10 degrees
/// sf::Angle a4 = 1.5_deg; // 1.5 degrees
/// sf::Angle a5 = 1_rad; // 1 radians
/// sf::Angle a6 = 3.14_rad; // 3.14 radians
/// \endcode
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,259 @@
////////////////////////////////////////////////////////////
//
// 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.
//
////////////////////////////////////////////////////////////
namespace priv
{
constexpr float pi = 3.141592654f;
constexpr float positiveRemainder(float a, float b)
{
assert(b > 0.0f);
const float val = a - static_cast<float>(static_cast<int>(a / b)) * b;
if (val >= 0.f)
return val;
else
return val + b;
}
}
////////////////////////////////////////////////////////////
constexpr Angle::Angle() :
m_degrees(0.0f)
{
}
////////////////////////////////////////////////////////////
constexpr float Angle::asDegrees() const
{
return m_degrees;
}
////////////////////////////////////////////////////////////
constexpr float Angle::asRadians() const
{
return m_degrees * (priv::pi / 180);
}
////////////////////////////////////////////////////////////
constexpr Angle Angle::wrapSigned() const
{
return degrees(priv::positiveRemainder(m_degrees + 180, 360) - 180);
}
////////////////////////////////////////////////////////////
constexpr Angle Angle::wrapUnsigned() const
{
return degrees(priv::positiveRemainder(m_degrees, 360));
}
////////////////////////////////////////////////////////////
constexpr Angle::Angle(float degrees) :
m_degrees(degrees)
{
}
////////////////////////////////////////////////////////////
constexpr Angle degrees(float angle)
{
return Angle(angle);
}
////////////////////////////////////////////////////////////
constexpr Angle radians(float angle)
{
return Angle(angle * (180 / priv::pi));
}
////////////////////////////////////////////////////////////
constexpr bool operator ==(Angle left, Angle right)
{
return left.asDegrees() == right.asDegrees();
}
////////////////////////////////////////////////////////////
constexpr bool operator !=(Angle left, Angle right)
{
return left.asDegrees() != right.asDegrees();
}
////////////////////////////////////////////////////////////
constexpr bool operator <(Angle left, Angle right)
{
return left.asDegrees() < right.asDegrees();
}
////////////////////////////////////////////////////////////
constexpr bool operator >(Angle left, Angle right)
{
return left.asDegrees() > right.asDegrees();
}
////////////////////////////////////////////////////////////
constexpr bool operator <=(Angle left, Angle right)
{
return left.asDegrees() <= right.asDegrees();
}
////////////////////////////////////////////////////////////
constexpr bool operator >=(Angle left, Angle right)
{
return left.asDegrees() >= right.asDegrees();
}
////////////////////////////////////////////////////////////
constexpr Angle operator -(Angle right)
{
return degrees(-right.asDegrees());
}
////////////////////////////////////////////////////////////
constexpr Angle operator +(Angle left, Angle right)
{
return degrees(left.asDegrees() + right.asDegrees());
}
////////////////////////////////////////////////////////////
constexpr Angle& operator +=(Angle& left, Angle right)
{
return left = left + right;
}
////////////////////////////////////////////////////////////
constexpr Angle operator -(Angle left, Angle right)
{
return degrees(left.asDegrees() - right.asDegrees());
}
////////////////////////////////////////////////////////////
constexpr Angle& operator -=(Angle& left, Angle right)
{
return left = left - right;
}
////////////////////////////////////////////////////////////
constexpr Angle operator *(Angle left, float right)
{
return degrees(left.asDegrees() * right);
}
////////////////////////////////////////////////////////////
constexpr Angle operator *(float left, Angle right)
{
return right * left;
}
////////////////////////////////////////////////////////////
constexpr Angle& operator *=(Angle& left, float right)
{
return left = left * right;
}
////////////////////////////////////////////////////////////
constexpr Angle operator /(Angle left, float right)
{
return degrees(left.asDegrees() / right);
}
////////////////////////////////////////////////////////////
constexpr Angle& operator /=(Angle& left, float right)
{
return left = left / right;
}
////////////////////////////////////////////////////////////
constexpr float operator /(Angle left, Angle right)
{
return left.asDegrees() / right.asDegrees();
}
////////////////////////////////////////////////////////////
constexpr Angle operator %(Angle left, Angle right)
{
return degrees(priv::positiveRemainder(left.asDegrees(), right.asDegrees()));
}
////////////////////////////////////////////////////////////
constexpr Angle& operator %=(Angle& left, Angle right)
{
return left = left % right;
}
namespace Literals
{
////////////////////////////////////////////////////////////
constexpr Angle operator "" _deg(long double angle)
{
return degrees(static_cast<float>(angle));
}
////////////////////////////////////////////////////////////
constexpr Angle operator "" _deg(unsigned long long angle)
{
return degrees(static_cast<float>(angle));
}
////////////////////////////////////////////////////////////
constexpr Angle operator "" _rad(long double angle)
{
return radians(static_cast<float>(angle));
}
////////////////////////////////////////////////////////////
constexpr Angle operator "" _rad(unsigned long long angle)
{
return radians(static_cast<float>(angle));
}
} // namespace Literals

View File

@ -72,11 +72,10 @@ std::size_t CircleShape::getPointCount() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Vector2f CircleShape::getPoint(std::size_t index) const Vector2f CircleShape::getPoint(std::size_t index) const
{ {
static const float pi = 3.141592654f; Angle angle = static_cast<float>(index) / static_cast<float>(m_pointCount) * sf::degrees(360) - sf::degrees(90);
float rad = angle.asRadians();
float angle = static_cast<float>(index) * 2.f * pi / static_cast<float>(m_pointCount) - pi / 2.f; float x = std::cos(rad) * m_radius;
float x = std::cos(angle) * m_radius; float y = std::sin(rad) * m_radius;
float y = std::sin(angle) * m_radius;
return Vector2f(m_radius + x, m_radius + y); return Vector2f(m_radius + x, m_radius + y);
} }

View File

@ -405,7 +405,7 @@ void Text::ensureGeometryUpdate() const
bool isBold = m_style & Bold; bool isBold = m_style & Bold;
bool isUnderlined = m_style & Underlined; bool isUnderlined = m_style & Underlined;
bool isStrikeThrough = m_style & StrikeThrough; bool isStrikeThrough = m_style & StrikeThrough;
float italicShear = (m_style & Italic) ? 0.209f : 0.f; // 12 degrees in radians float italicShear = (m_style & Italic) ? sf::degrees(12).asRadians() : 0.f;
float underlineOffset = m_font->getUnderlinePosition(m_characterSize); float underlineOffset = m_font->getUnderlinePosition(m_characterSize);
float underlineThickness = m_font->getUnderlineThickness(m_characterSize); float underlineThickness = m_font->getUnderlineThickness(m_characterSize);

View File

@ -163,9 +163,9 @@ Transform& Transform::translate(const Vector2f& offset)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Transform& Transform::rotate(float angle) Transform& Transform::rotate(Angle angle)
{ {
float rad = angle * 3.141592654f / 180.f; float rad = angle.asRadians();
float cos = std::cos(rad); float cos = std::cos(rad);
float sin = std::sin(rad); float sin = std::sin(rad);
@ -178,9 +178,9 @@ Transform& Transform::rotate(float angle)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Transform& Transform::rotate(float angle, const Vector2f& center) Transform& Transform::rotate(Angle angle, const Vector2f& center)
{ {
float rad = angle * 3.141592654f / 180.f; float rad = angle.asRadians();
float cos = std::cos(rad); float cos = std::cos(rad);
float sin = std::sin(rad); float sin = std::sin(rad);

View File

@ -35,7 +35,7 @@ namespace sf
Transformable::Transformable() : Transformable::Transformable() :
m_origin (0, 0), m_origin (0, 0),
m_position (0, 0), m_position (0, 0),
m_rotation (0), m_rotation (),
m_scale (1, 1), m_scale (1, 1),
m_transform (), m_transform (),
m_transformNeedUpdate (true), m_transformNeedUpdate (true),
@ -61,11 +61,9 @@ void Transformable::setPosition(const Vector2f& position)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Transformable::setRotation(float angle) void Transformable::setRotation(Angle angle)
{ {
m_rotation = std::fmod(angle, 360.f); m_rotation = angle.wrapUnsigned();
if (m_rotation < 0)
m_rotation += 360.f;
m_transformNeedUpdate = true; m_transformNeedUpdate = true;
m_inverseTransformNeedUpdate = true; m_inverseTransformNeedUpdate = true;
@ -98,7 +96,7 @@ const Vector2f& Transformable::getPosition() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
float Transformable::getRotation() const Angle Transformable::getRotation() const
{ {
return m_rotation; return m_rotation;
} }
@ -126,7 +124,7 @@ void Transformable::move(const Vector2f& offset)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Transformable::rotate(float angle) void Transformable::rotate(Angle angle)
{ {
setRotation(m_rotation + angle); setRotation(m_rotation + angle);
} }
@ -145,7 +143,7 @@ const Transform& Transformable::getTransform() const
// Recompute the combined transform if needed // Recompute the combined transform if needed
if (m_transformNeedUpdate) if (m_transformNeedUpdate)
{ {
float angle = -m_rotation * 3.141592654f / 180.f; float angle = -m_rotation.asRadians();
float cosine = std::cos(angle); float cosine = std::cos(angle);
float sine = std::sin(angle); float sine = std::sin(angle);
float sxc = m_scale.x * cosine; float sxc = m_scale.x * cosine;

View File

@ -35,7 +35,7 @@ namespace sf
View::View() : View::View() :
m_center (), m_center (),
m_size (), m_size (),
m_rotation (0), m_rotation (),
m_viewport ({0, 0}, {1, 1}), m_viewport ({0, 0}, {1, 1}),
m_transformUpdated (false), m_transformUpdated (false),
m_invTransformUpdated(false) m_invTransformUpdated(false)
@ -48,7 +48,7 @@ m_invTransformUpdated(false)
View::View(const FloatRect& rectangle) : View::View(const FloatRect& rectangle) :
m_center (), m_center (),
m_size (), m_size (),
m_rotation (0), m_rotation (),
m_viewport ({0, 0}, {1, 1}), m_viewport ({0, 0}, {1, 1}),
m_transformUpdated (false), m_transformUpdated (false),
m_invTransformUpdated(false) m_invTransformUpdated(false)
@ -61,7 +61,7 @@ m_invTransformUpdated(false)
View::View(const Vector2f& center, const Vector2f& size) : View::View(const Vector2f& center, const Vector2f& size) :
m_center (center), m_center (center),
m_size (size), m_size (size),
m_rotation (0), m_rotation (),
m_viewport ({0, 0}, {1, 1}), m_viewport ({0, 0}, {1, 1}),
m_transformUpdated (false), m_transformUpdated (false),
m_invTransformUpdated(false) m_invTransformUpdated(false)
@ -98,11 +98,9 @@ void View::setSize(const Vector2f& size)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void View::setRotation(float angle) void View::setRotation(Angle angle)
{ {
m_rotation = std::fmod(angle, 360.f); m_rotation = angle.wrapUnsigned();
if (m_rotation < 0)
m_rotation += 360.f;
m_transformUpdated = false; m_transformUpdated = false;
m_invTransformUpdated = false; m_invTransformUpdated = false;
@ -123,7 +121,7 @@ void View::reset(const FloatRect& rectangle)
m_center.y = rectangle.top + rectangle.height / 2.f; m_center.y = rectangle.top + rectangle.height / 2.f;
m_size.x = rectangle.width; m_size.x = rectangle.width;
m_size.y = rectangle.height; m_size.y = rectangle.height;
m_rotation = 0; m_rotation = Angle::Zero;
m_transformUpdated = false; m_transformUpdated = false;
m_invTransformUpdated = false; m_invTransformUpdated = false;
@ -145,7 +143,7 @@ const Vector2f& View::getSize() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
float View::getRotation() const Angle View::getRotation() const
{ {
return m_rotation; return m_rotation;
} }
@ -166,7 +164,7 @@ void View::move(const Vector2f& offset)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void View::rotate(float angle) void View::rotate(Angle angle)
{ {
setRotation(m_rotation + angle); setRotation(m_rotation + angle);
} }
@ -186,7 +184,7 @@ const Transform& View::getTransform() const
if (!m_transformUpdated) if (!m_transformUpdated)
{ {
// Rotation components // Rotation components
float angle = m_rotation * 3.141592654f / 180.f; float angle = m_rotation.asRadians();
float cosine = std::cos(angle); float cosine = std::cos(angle);
float sine = std::sin(angle); float sine = std::sin(angle);
float tx = -m_center.x * cosine - m_center.y * sine + m_center.x; float tx = -m_center.x * cosine - m_center.y * sine + m_center.x;

38
src/SFML/System/Angle.cpp Normal file
View File

@ -0,0 +1,38 @@
////////////////////////////////////////////////////////////
//
// 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.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/System/Angle.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
const Angle Angle::Zero;
} // namespace sf

View File

@ -4,6 +4,8 @@ set(SRCROOT ${PROJECT_SOURCE_DIR}/src/SFML/System)
# all source files # all source files
set(SRC set(SRC
${SRCROOT}/Angle.cpp
${INCROOT}/Angle.hpp
${SRCROOT}/Clock.cpp ${SRCROOT}/Clock.cpp
${INCROOT}/Clock.hpp ${INCROOT}/Clock.hpp
${SRCROOT}/Err.cpp ${SRCROOT}/Err.cpp

View File

@ -25,6 +25,7 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Headers // Headers
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
#include <SFML/System/Angle.hpp>
#include <SFML/Window/SensorImpl.hpp> #include <SFML/Window/SensorImpl.hpp>
#include <SFML/Window/iOS/SFAppDelegate.hpp> #include <SFML/Window/iOS/SFAppDelegate.hpp>
@ -35,7 +36,7 @@ namespace
float toDegrees(float radians) float toDegrees(float radians)
{ {
return radians * 180.f / 3.141592654f; return sf::radians(radians).asDegrees();
} }
} }

View File

@ -6,6 +6,7 @@ target_compile_features(sfml-test-main PRIVATE cxx_std_17)
# System is always built # System is always built
SET(SYSTEM_SRC SET(SYSTEM_SRC
"${SRCROOT}/System/Angle.cpp"
"${SRCROOT}/System/Clock.cpp" "${SRCROOT}/System/Clock.cpp"
"${SRCROOT}/System/FileInputStream.cpp" "${SRCROOT}/System/FileInputStream.cpp"
"${SRCROOT}/System/Time.cpp" "${SRCROOT}/System/Time.cpp"

View File

@ -123,7 +123,7 @@ TEST_CASE("sf::Transform class - [graphics]")
SUBCASE("Around origin") SUBCASE("Around origin")
{ {
sf::Transform transform; sf::Transform transform;
transform.rotate(90); transform.rotate(sf::degrees(90));
CHECK(transform.getMatrix()[0] == Approx(0)); CHECK(transform.getMatrix()[0] == Approx(0));
CHECK(transform.getMatrix()[4] == Approx(-1)); CHECK(transform.getMatrix()[4] == Approx(-1));
CHECK(transform.getMatrix()[12] == Approx(0)); CHECK(transform.getMatrix()[12] == Approx(0));
@ -138,7 +138,7 @@ TEST_CASE("sf::Transform class - [graphics]")
SUBCASE("Around custom point") SUBCASE("Around custom point")
{ {
sf::Transform transform; sf::Transform transform;
transform.rotate(90, {1.0f, 0.0f}); transform.rotate(sf::degrees(90), {1.0f, 0.0f});
CHECK(transform.getMatrix()[0] == Approx(0)); CHECK(transform.getMatrix()[0] == Approx(0));
CHECK(transform.getMatrix()[4] == Approx(-1)); CHECK(transform.getMatrix()[4] == Approx(-1));
CHECK(transform.getMatrix()[12] == Approx(1)); CHECK(transform.getMatrix()[12] == Approx(1));

302
test/System/Angle.cpp Normal file
View File

@ -0,0 +1,302 @@
#include <SFML/System/Angle.hpp>
#include "SystemUtil.hpp"
#include <doctest.h>
using doctest::Approx;
TEST_CASE("sf::Angle class - [system]")
{
SUBCASE("Construction")
{
SUBCASE("Default constructor")
{
const sf::Angle angle;
CHECK(angle.asDegrees() == 0);
CHECK(angle.asRadians() == 0);
}
SUBCASE("wrapSigned()")
{
CHECK(sf::Angle::Zero.wrapSigned() == sf::Angle::Zero);
CHECK(sf::degrees(0).wrapSigned() == sf::degrees(0));
CHECK(sf::degrees(1).wrapSigned() == sf::degrees(1));
CHECK(sf::degrees(-1).wrapSigned() == sf::degrees(-1));
CHECK(sf::degrees(90).wrapSigned() == sf::degrees(90));
CHECK(sf::degrees(-90).wrapSigned() == sf::degrees(-90));
CHECK(sf::degrees(180).wrapSigned() == sf::degrees(-180));
CHECK(sf::degrees(-180).wrapSigned() == sf::degrees(-180));
CHECK(sf::degrees(360).wrapSigned() == sf::degrees(0));
CHECK(sf::degrees(-360).wrapSigned() == sf::degrees(0));
CHECK(sf::degrees(720).wrapSigned() == sf::degrees(0));
CHECK(sf::degrees(-720).wrapSigned() == sf::degrees(0));
}
SUBCASE("wrapUnsigned()")
{
CHECK(sf::Angle::Zero.wrapUnsigned() == sf::Angle::Zero);
CHECK(sf::degrees(0).wrapUnsigned() == sf::degrees(0));
CHECK(sf::degrees(1).wrapUnsigned() == sf::degrees(1));
CHECK(sf::degrees(-1).wrapUnsigned() == sf::degrees(359));
CHECK(sf::degrees(90).wrapUnsigned() == sf::degrees(90));
CHECK(sf::degrees(-90).wrapUnsigned() == sf::degrees(270));
CHECK(sf::degrees(180).wrapUnsigned() == sf::degrees(180));
CHECK(sf::degrees(-180).wrapUnsigned() == sf::degrees(180));
CHECK(sf::degrees(360).wrapUnsigned() == sf::degrees(0));
CHECK(sf::degrees(-360).wrapUnsigned() == sf::degrees(0));
CHECK(sf::degrees(720).wrapUnsigned() == sf::degrees(0));
CHECK(sf::degrees(-720).wrapUnsigned() == sf::degrees(0));
}
SUBCASE("degrees()")
{
const sf::Angle angle = sf::degrees(15);
CHECK(angle == sf::degrees(15));
CHECK(angle.asRadians() == Approx(0.26179939f));
const sf::Angle bigAngle = sf::degrees(1000);
CHECK(bigAngle == sf::degrees(1000));
CHECK(bigAngle.asRadians() == Approx(17.453293f));
const sf::Angle bigNegativeAngle = sf::degrees(-4321);
CHECK(bigNegativeAngle == sf::degrees(-4321));
CHECK(bigNegativeAngle.asRadians() == Approx(-75.415677f));
}
SUBCASE("radians()")
{
const sf::Angle angle = sf::radians(1);
CHECK(angle.asDegrees() == Approx(57.2957795f));
CHECK(angle.asRadians() == Approx(1.0f));
const sf::Angle bigAngle = sf::radians(72);
CHECK(bigAngle.asDegrees() == Approx(4125.29612f));
CHECK(bigAngle.asRadians() == Approx(72.0f));
const sf::Angle bigNegativeAngle = sf::radians(-200);
CHECK(bigNegativeAngle.asDegrees() == Approx(-11459.1559f));
CHECK(bigNegativeAngle.asRadians() == Approx(-200.0f));
}
}
SUBCASE("Constants")
{
CHECK(sf::Angle::Zero.asDegrees() == 0);
CHECK(sf::Angle::Zero.asRadians() == 0);
}
SUBCASE("Operators")
{
SUBCASE("operator==")
{
CHECK(sf::Angle() == sf::Angle());
CHECK(sf::Angle() == sf::Angle::Zero);
CHECK(sf::Angle() == sf::degrees(0));
CHECK(sf::Angle() == sf::radians(0));
CHECK(sf::degrees(0) == sf::radians(0));
CHECK(sf::degrees(15) == sf::degrees(15));
CHECK(sf::radians(1) == sf::radians(1));
CHECK(sf::degrees(360) == sf::degrees(360));
CHECK(sf::degrees(720) == sf::degrees(720));
}
SUBCASE("operator!=")
{
CHECK(sf::Angle() != sf::radians(2));
CHECK(sf::degrees(1) != sf::radians(1));
CHECK(sf::radians(0) != sf::radians(0.1f));
}
SUBCASE("operator<")
{
CHECK(sf::radians(0) < sf::degrees(0.1f));
CHECK(sf::degrees(0) < sf::radians(0.1f));
CHECK(sf::radians(-0.1f) < sf::radians(0));
CHECK(sf::degrees(-0.1f) < sf::degrees(0));
}
SUBCASE("operator>")
{
CHECK(sf::radians(0.1f) > sf::degrees(0));
CHECK(sf::degrees(0.1f) > sf::radians(0));
CHECK(sf::radians(0) > sf::radians(-0.1f));
CHECK(sf::degrees(0) > sf::degrees(-0.1f));
}
SUBCASE("operator<=")
{
CHECK(sf::radians(0) <= sf::degrees(0.1f));
CHECK(sf::degrees(0) <= sf::radians(0.1f));
CHECK(sf::radians(-0.1f) <= sf::radians(0));
CHECK(sf::degrees(-0.1f) <= sf::degrees(0));
CHECK(sf::Angle() <= sf::Angle());
CHECK(sf::Angle() <= sf::Angle::Zero);
CHECK(sf::Angle() <= sf::degrees(0));
CHECK(sf::Angle() <= sf::radians(0));
CHECK(sf::degrees(0) <= sf::radians(0));
CHECK(sf::degrees(15) <= sf::degrees(15));
CHECK(sf::radians(1) <= sf::radians(1));
CHECK(sf::degrees(360) <= sf::degrees(360));
CHECK(sf::degrees(720) <= sf::degrees(720));
}
SUBCASE("operator>=")
{
CHECK(sf::radians(0.1f) >= sf::degrees(0));
CHECK(sf::degrees(0.1f) >= sf::radians(0));
CHECK(sf::radians(0) >= sf::radians(-0.1f));
CHECK(sf::degrees(0) >= sf::degrees(-0.1f));
CHECK(sf::Angle() >= sf::Angle());
CHECK(sf::Angle() >= sf::Angle::Zero);
CHECK(sf::Angle() >= sf::degrees(0));
CHECK(sf::Angle() >= sf::radians(0));
CHECK(sf::degrees(0) >= sf::radians(0));
CHECK(sf::degrees(15) >= sf::degrees(15));
CHECK(sf::radians(1) >= sf::radians(1));
CHECK(sf::degrees(360) >= sf::degrees(360));
CHECK(sf::degrees(720) >= sf::degrees(720));
}
SUBCASE("Unary operator-")
{
CHECK(-sf::Angle() == sf::Angle());
CHECK(-sf::radians(-1) == sf::radians(1));
CHECK(-sf::degrees(15) == sf::degrees(-15));
CHECK(-sf::radians(1) == sf::radians(-1));
}
SUBCASE("operator+")
{
CHECK(sf::Angle() + sf::Angle() == sf::Angle());
CHECK(sf::Angle::Zero + sf::radians(0.5f) == sf::radians(0.5f));
CHECK(sf::radians(6) + sf::radians(0.5f) == sf::radians(6.5f));
CHECK(sf::radians(10) + sf::radians(0.5f) == sf::radians(10.5f));
CHECK(sf::degrees(360) + sf::degrees(360) == sf::degrees(720));
}
SUBCASE("operator+=")
{
sf::Angle angle = sf::degrees(-15);
angle += sf::degrees(15);
CHECK(angle == sf::degrees(0));
angle += sf::radians(10);
CHECK(angle == sf::radians(10));
}
SUBCASE("operator-")
{
CHECK(sf::Angle() - sf::Angle() == sf::Angle());
CHECK(sf::radians(1) - sf::radians(0.5f) == sf::radians(0.5f));
CHECK(sf::Angle::Zero - sf::radians(0.5f) == sf::radians(-0.5f));
CHECK(sf::degrees(900) - sf::degrees(1) == sf::degrees(899));
}
SUBCASE("operator-=")
{
sf::Angle angle = sf::degrees(15);
angle -= sf::degrees(15);
CHECK(angle == sf::degrees(0));
angle -= sf::radians(10);
CHECK(angle == sf::radians(-10));
}
SUBCASE("operator*")
{
CHECK(sf::radians(0) * 10 == sf::Angle::Zero);
CHECK(sf::degrees(10) * 2.5f == sf::degrees(25));
CHECK(sf::degrees(100) * 10.0f == sf::degrees(1000));
CHECK(10 * sf::radians(0) == sf::Angle::Zero);
CHECK(2.5f * sf::degrees(10) == sf::degrees(25));
CHECK(10.0f * sf::degrees(100) == sf::degrees(1000));
}
SUBCASE("operator*=")
{
sf::Angle angle = sf::degrees(1);
angle *= 10;
CHECK(angle == sf::degrees(10));
}
SUBCASE("operator/")
{
CHECK(sf::Angle::Zero / 10 == sf::Angle::Zero);
CHECK(sf::degrees(10) / 2.5f == sf::degrees(4));
CHECK(sf::radians(12) / 3 == sf::radians(4));
CHECK(sf::Angle::Zero / sf::degrees(1) == 0);
CHECK(sf::degrees(10) / sf::degrees(10) == 1);
CHECK(sf::radians(10) / sf::radians(2) == Approx(5.0f));
}
SUBCASE("operator/=")
{
sf::Angle angle = sf::degrees(60);
angle /= 5;
CHECK(angle == sf::degrees(12));
}
SUBCASE("operator%")
{
CHECK(sf::Angle::Zero % sf::radians(0.5f) == sf::Angle::Zero);
CHECK(sf::radians(10) % sf::radians(1) == sf::radians(0));
CHECK(sf::degrees(90) % sf::degrees(30) == sf::degrees(0));
CHECK(sf::degrees(90) % sf::degrees(40) == sf::degrees(10));
CHECK(sf::degrees(-90) % sf::degrees(30) == sf::degrees(0));
CHECK(sf::degrees(-90) % sf::degrees(40) == sf::degrees(30));
}
SUBCASE("operator%=")
{
sf::Angle angle = sf::degrees(59);
angle %= sf::degrees(10);
CHECK(angle == sf::degrees(9));
}
SUBCASE("operator _deg")
{
using namespace sf::Literals;
CHECK(0.0_deg == sf::Angle::Zero);
CHECK(1.0_deg == sf::degrees(1));
CHECK(-1.0_deg == sf::degrees(-1));
CHECK(3.14_deg == sf::degrees(3.14f));
CHECK(-3.14_deg == sf::degrees(-3.14f));
CHECK(0_deg == sf::Angle::Zero);
CHECK(1_deg == sf::degrees(1));
CHECK(-1_deg == sf::degrees(-1));
CHECK(100_deg == sf::degrees(100));
CHECK(-100_deg == sf::degrees(-100));
}
SUBCASE("operator _rad")
{
using namespace sf::Literals;
CHECK(0.0_rad == sf::Angle::Zero);
CHECK(1.0_rad == sf::radians(1));
CHECK(-1.0_rad ==sf::radians(-1));
CHECK(3.14_rad == sf::radians(3.14f));
CHECK(-3.14_rad == sf::radians(-3.14f));
CHECK(0_rad == sf::Angle::Zero);
CHECK(1_rad == sf::radians(1));
CHECK(-1_rad == sf::radians(-1));
CHECK(100_rad ==sf::radians(100));
CHECK(-100_rad ==sf::radians(-100));
}
}
SUBCASE("Constexpr support")
{
constexpr auto result = []
{
sf::Angle angle = sf::degrees(9);
angle %= sf::degrees(2);
return angle;
}();
static_assert(result == sf::degrees(1));
}
}

View File

@ -1,5 +1,6 @@
#include "SystemUtil.hpp" #include "SystemUtil.hpp"
#include <SFML/System/Angle.hpp>
#include <SFML/System/String.hpp> #include <SFML/System/String.hpp>
#include <SFML/System/Time.hpp> #include <SFML/System/Time.hpp>
@ -8,13 +9,22 @@
#include <filesystem> #include <filesystem>
#endif // !defined(__GNUC__) || (__GNUC__ >= 9) #endif // !defined(__GNUC__) || (__GNUC__ >= 9)
#include <cassert>
#include <fstream> #include <fstream>
#include <iomanip>
#include <limits>
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
#include <cassert>
namespace sf namespace sf
{ {
std::ostream& operator <<(std::ostream& os, const sf::Angle& angle)
{
os << std::fixed << std::setprecision(std::numeric_limits<float>::max_digits10);
os << angle.asDegrees() << " deg";
return os;
}
std::ostream& operator <<(std::ostream& os, const sf::String& string) std::ostream& operator <<(std::ostream& os, const sf::String& string)
{ {
os << string.toAnsiString(); os << string.toAnsiString();

View File

@ -16,9 +16,11 @@
// String conversions for doctest framework // String conversions for doctest framework
namespace sf namespace sf
{ {
class Angle;
class String; class String;
class Time; class Time;
std::ostream& operator <<(std::ostream& os, const sf::Angle& angle);
std::ostream& operator <<(std::ostream& os, const sf::String& string); std::ostream& operator <<(std::ostream& os, const sf::String& string);
std::ostream& operator <<(std::ostream& os, sf::Time time); std::ostream& operator <<(std::ostream& os, sf::Time time);