Compare commits

...
Sign in to create a new pull request.

1 commit
master ... tp4

Author SHA1 Message Date
38db780844 working controller 2024-05-31 03:02:45 +02:00
10 changed files with 317 additions and 44 deletions

View file

@ -15,6 +15,7 @@ add_executable(robotCommand
src/jsonrpctcpclient.cpp src/jsonrpctcpclient.cpp
src/PosTracker.cpp src/PosTracker.cpp
src/PosHistory.cpp src/PosHistory.cpp
src/simpleLocTracker.cpp
) )
find_package(jsoncpp REQUIRED) find_package(jsoncpp REQUIRED)

14
src/ILocationProvider.hpp Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include <SFML/System/Vector2.hpp>
#include <cstddef>
class ILocationProvider {
public:
virtual sf::Vector2f get_pos() = 0;
virtual float get_rot() = 0;
virtual void update(std::size_t value_left, std::size_t value_right) = 0;
virtual void reset(sf::Vector2f pos, float rot) = 0;
virtual void hint_right(bool forward) = 0;
virtual void hint_left(bool forward) = 0;
};

View file

@ -1,4 +1,5 @@
#include "PosTracker.hpp" #include "PosTracker.hpp"
#include <SFML/System/Vector2.hpp>
using sf::Vector2f; using sf::Vector2f;
using sf::Transform; using sf::Transform;
@ -7,12 +8,12 @@ void PosTracker::reset(Vector2f initialPos, float initialRot) {
transform = transformFromPosRot(initialPos, initialRot); transform = transformFromPosRot(initialPos, initialRot);
} }
void PosTracker::update(float newLeftValue, float newRightValue) { void PosTracker::update(std::size_t value_left, std::size_t value_right) {
auto leftDistance = (newLeftValue - _lastLeftValue) * wheelIncrementDistance(); auto leftDistance = (value_left - _lastLeftValue) * wheelIncrementDistance() * _wheel_dir_left;
auto rightDistance = (newRightValue - _lastRightValue) * wheelIncrementDistance(); auto rightDistance = (value_right - _lastRightValue) * wheelIncrementDistance() * _wheel_dir_right;
updateTransform(leftDistance, rightDistance); updateTransform(leftDistance, rightDistance);
_lastLeftValue = newLeftValue; _lastLeftValue = value_left;
_lastRightValue = newRightValue; _lastRightValue = value_right;
} }
void PosTracker::updateTransform(float leftDistance, float rightDistance) { void PosTracker::updateTransform(float leftDistance, float rightDistance) {
@ -26,3 +27,13 @@ void PosTracker::updateTransform(float leftDistance, float rightDistance) {
); );
transform = new_; transform = new_;
} }
Vector2f PosTracker::get_pos() { return this->transform.getPosition(); }
float PosTracker::get_rot() { return this->transform.getRotation(); }
void PosTracker::hint_right(bool forward) {
_wheel_dir_right = forward ? 1 : -1;
}
void PosTracker::hint_left(bool forward) {
_wheel_dir_left = forward ? 1 : -1;
}

View file

@ -7,6 +7,8 @@
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <SFML/Window/Event.hpp> #include <SFML/Window/Event.hpp>
# include "ILocationProvider.hpp"
inline sf::Transformable transformFromPosRot(sf::Vector2f position, float rotation) { inline sf::Transformable transformFromPosRot(sf::Vector2f position, float rotation) {
auto result = sf::Transformable(); auto result = sf::Transformable();
result.setPosition(position); result.setPosition(position);
@ -14,26 +16,30 @@ inline sf::Transformable transformFromPosRot(sf::Vector2f position, float rotati
return result; return result;
} }
class PosTracker { class PosTracker : public ILocationProvider {
/// Wheel encoder values. /// Wheel encoder values.
float _wheelDistance, _wheelDiameter; float _wheelDistance, _wheelDiameter;
float _wheelStepsPerTurn; float _wheelStepsPerTurn;
float _lastLeftValue, _lastRightValue; float _lastLeftValue, _lastRightValue;
float _wheel_dir_left, _wheel_dir_right;
sf::Transformable transform; sf::Transformable transform;
public: public:
PosTracker(sf::Vector2f initialPos, float initialRot, float wheelDistance, float wheelDiameter, float wheelStepsPerTurn) : PosTracker(sf::Vector2f initialPos, float initialRot, float wheelDistance,
_wheelDistance(wheelDistance), float wheelDiameter, float wheelStepsPerTurn)
_wheelDiameter(wheelDiameter), : _wheelDistance(wheelDistance), _wheelDiameter(wheelDiameter),
_wheelStepsPerTurn(wheelStepsPerTurn), _wheelStepsPerTurn(wheelStepsPerTurn), _lastLeftValue(0.),
_lastLeftValue(0.),
_lastRightValue(0.), _lastRightValue(0.),
_wheel_dir_left(1.),
_wheel_dir_right(1.),
transform(transformFromPosRot(initialPos, initialRot)) transform(transformFromPosRot(initialPos, initialRot))
{} {}
void reset(sf::Vector2f initialPos, float initialRot); void reset(sf::Vector2f initialPos, float initialRot) override;
void update(float newLeftValue, float newRightValue); void update(std::size_t value_left, std::size_t value_right) override;
virtual void hint_right(bool forward) override;
virtual void hint_left(bool forward) override;
inline sf::Vector2f getPosition() const { return transform.getPosition(); } float get_rot() override;
inline float getRotation() const { return transform.getRotation(); } sf::Vector2f get_pos() override;
private: private:
inline float wheelIncrementDistance() const { inline float wheelIncrementDistance() const {
return _wheelDiameter * std::numbers::pi / _wheelStepsPerTurn; return _wheelDiameter * std::numbers::pi / _wheelStepsPerTurn;

30
src/SimpleLocTracker.hpp Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include "ILocationProvider.hpp"
#include <SFML/System/Vector2.hpp>
#include <optional>
struct Mission {
sf::Vector2f from_pos, to_pos;
float from_rot, to_rot;
int total_encodings;
int current_encodings;
inline float progress() { return float(current_encodings) / float(total_encodings); }
};
class SimpleLocTracker : public ILocationProvider {
std::optional<Mission> current_mission;
sf::Vector2f last_pos;
float last_rot;
int last_step;
public:
inline SimpleLocTracker(sf::Vector2f pos, float rot) : last_pos(pos), last_rot(rot) {}
void start(sf::Vector2f to_pos, float to_rot, int total_encodings);
void progress();
sf::Vector2f get_pos() override;
float get_rot() override;
void update(std::size_t value_left, std::size_t value_right) override {};
void reset(sf::Vector2f pos, float rot) override{};
};

View file

@ -1,5 +1,7 @@
#include "ILocationProvider.hpp"
#include "PosHistory.hpp" #include "PosHistory.hpp"
#include "PosTracker.hpp" #include "PosTracker.hpp"
#include "SimpleLocTracker.hpp"
#include "filter.hpp" #include "filter.hpp"
#include "robot.hpp" #include "robot.hpp"
#include "utils.hpp" #include "utils.hpp"
@ -14,9 +16,13 @@
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <SFML/Window/Event.hpp> #include <SFML/Window/Event.hpp>
#include <SFML/Window/Keyboard.hpp> #include <SFML/Window/Keyboard.hpp>
#include <cmath>
#include <csignal> #include <csignal>
#include <iostream> #include <iostream>
#include <numbers>
#include <sstream> #include <sstream>
#include <thread>
#include <unistd.h>
using sf::Vector2f; using sf::Vector2f;
@ -39,13 +45,25 @@ void controller_follow_border(Robot &robot, float speed_factor) {
} }
} }
std::tuple<float, float> calc_path(sf::Vector2f pos, float rot, sf::Vector2f dest) {
auto delta = dest - pos;
auto abs_angle = std::atan2(delta.y, delta.x);
auto angle = abs_angle - rot;
auto distance = vectorLength(delta);
DBG(delta.x);
DBG(delta.y);
return { angle, distance };
}
// note : Ne marche pas car remplace le stack du code interrompu. // note : Ne marche pas car remplace le stack du code interrompu.
static bool do_stop = false; static bool do_stop = false;
void stop_handler(int) { void stop_handler(int) {
do_stop = true; do_stop = true;
} }
void event(sf::RenderWindow & window, Robot& robot) { void event(sf::RenderWindow & window, Robot& robot, ILocationProvider& pos) {
bool do_stop_ = do_stop; bool do_stop_ = do_stop;
sf::Event event; sf::Event event;
while (window.pollEvent(event)) { while (window.pollEvent(event)) {
@ -55,6 +73,15 @@ void event(sf::RenderWindow & window, Robot& robot) {
} }
if (event.type == sf::Event::Closed) do_stop_ = true; if (event.type == sf::Event::Closed) do_stop_ = true;
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) do_stop_ = true; if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) do_stop_ = true;
if (event.type == sf::Event::MouseButtonPressed) {
auto click = event.mouseButton;
// auto [rotation, distance] = calc_path(pos.get_pos(), pos.get_rot(), sf::Vector2f(click.x, click.y));
auto [rotation, distance] = calc_path({400, 300}, (std::numbers::pi / 2), sf::Vector2f(800 - click.x, 600 - click.y));
DBG(rotation);
DBG(distance);
robot.doRotate(rotation);
robot.doStraightLine(distance * 5);
}
} }
if (do_stop_) { if (do_stop_) {
robot.setMotorPower(Robot::MotorIndex::LEFT, 0); robot.setMotorPower(Robot::MotorIndex::LEFT, 0);
@ -74,51 +101,56 @@ class ControlWindow {
Robot& _robot; Robot& _robot;
sf::RenderWindow _window; sf::RenderWindow _window;
sf::RectangleShape _robotShape; sf::RectangleShape _robotShape;
PosTracker _tracker; ILocationProvider& _location;
PosHistory _history; PosHistory _history;
MotorJitterFilter _filter;
float _speed_factor; float _speed_factor;
public: public:
ControlWindow(Robot& robot, Vector2f robot_size, float wheel_diameter, float latice_count, int history_count, float speed_factor, float filter_delay) : ControlWindow(Robot &robot, Vector2f robot_size, float wheel_diameter,
_robot(robot), float latice_count, int history_count, float speed_factor,
_window(sf::RenderWindow( ILocationProvider &location)
sf::VideoMode(800u, 600u), : _robot(robot),
"robot-command", _window(sf::RenderWindow(sf::VideoMode(800u, 600u), "robot-command",
sf::Style::Titlebar | sf::Style::Close sf::Style::Titlebar | sf::Style::Close)),
)),
_robotShape(sf::RectangleShape(robot_size)), _robotShape(sf::RectangleShape(robot_size)),
_tracker(PosTracker(Vector2f(90, 90), 0., robot_size.y, wheel_diameter, latice_count)), _location(location),
_history(PosHistory(history_count)), _history(PosHistory(history_count)),
_speed_factor(speed_factor), _speed_factor(speed_factor)
_filter(filter_delay)
{ {
_window.setVerticalSyncEnabled(true); _window.setVerticalSyncEnabled(true);
_robotShape.setOrigin(sf::Vector2f(robot_size.x / 2, robot_size.y / 2)); _robotShape.setOrigin(sf::Vector2f(robot_size.x / 2, robot_size.y / 2));
_robotShape.setFillColor(sf::Color::Blue); _robotShape.setFillColor(sf::Color::Blue);
_tracker.update(robot.getEncoderWheelValue(1), robot.getEncoderWheelValue(0)); }
_tracker.reset(Vector2f(90, 90), 0.);
void reset(Vector2f initial_pos, float initial_rot) {
_robot.doStraightLine(20);
sleep(2);
_location.update(DBG(_robot.getEncoderWheelValue(1)), DBG(_robot.getEncoderWheelValue(0)));
_location.reset(initial_pos, initial_rot);
} }
void run() { void run() {
while (_window.isOpen()) { while (_window.isOpen()) {
update(); update();
controller_follow_border_step(_robot, _speed_factor); // controller_follow_border_step(_robot, _speed_factor);
usleep(20 * 1000);
} }
} }
private: private:
void update() { void update() {
_tracker.update(_robot.getEncoderWheelValue(1), _robot.getEncoderWheelValue(0)); auto left = _robot.getEncoderWheelValue(1);
auto right = _robot.getEncoderWheelValue(0);
_location.update(left, right);
_robotShape.setPosition(_tracker.getPosition()); _robotShape.setPosition(scaled(_location.get_pos(), 0.2));
_robotShape.setRotation(_tracker.getRotation()); _robotShape.setRotation(rad_to_deg(_location.get_rot()));
_history.append(_tracker.getPosition()); _history.append(_location.get_pos());
event(_window, _robot); event(_window, _robot, _location);
_window.clear(); _window.clear();
_window.draw(_robotShape); // _window.draw(_robotShape);
_window.draw(_history.vertices()); // _window.draw(_history.vertices());
_window.display(); _window.display();
} }
}; };
@ -139,15 +171,32 @@ int main(int argc, char ** argv)
} }
std::cout << "Try to connect to " << hostIpAddress << ":" << tcpPort << " ..." << std::endl; std::cout << "Try to connect to " << hostIpAddress << ":" << tcpPort << " ..." << std::endl;
Robot robot(hostIpAddress, tcpPort); // auto location = new SimpleLocTracker(Vector2f(300, 300), 0);
robot.waitReady(); auto location = new PosTracker(Vector2f(300, 300), 0, ROBOT_SIZE, WHEEL_DIAMETER, 20);
auto robot = new Robot(hostIpAddress, tcpPort, location);
// robot.waitReady();
usleep(100 * 1000);
std::thread([](Robot* robot){
usleep(2 * 1000 * 1000);
// robot->doStraightLine(500);
// robot->doRotate(std::numbers::pi);
// robot->doStraightLine(500);
}, robot).detach();
try { try {
// #define STEP_1 // #define STEP_1
#ifdef STEP_1 #ifdef STEP_1
controller_follow_border(robot); controller_follow_border(robot);
#else #else
ControlWindow(robot, Vector2f(ROBOT_SIZE, ROBOT_SIZE), WHEEL_DIAMETER, LATICE_COUNT, 4000, 0.78, 1).run(); auto control = ControlWindow(
*robot,
Vector2f(13, ROBOT_SIZE), WHEEL_DIAMETER, LATICE_COUNT,
4000, 0.78,
*location
);
control.reset(Vector2f(300, 300), 0.);
control.run();
#endif #endif
} catch (std::exception &e) { } catch (std::exception &e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;

View file

@ -1,9 +1,33 @@
#include "robot.hpp" #include "robot.hpp"
#include "ILocationProvider.hpp"
#include "SimpleLocTracker.hpp"
#include "utils.hpp"
#include <SFML/System/Vector2.hpp>
#include <cmath>
#include <json/value.h> #include <json/value.h>
Robot::Robot(const std::string & hostIpAddress, uint16_t tcpPort) sf::Vector2f move_from(sf::Vector2f from, float rot, float distance) {
float x = std::cos(rot) * distance;
float y = std::sin(rot) * distance;
return sf::Vector2f(from.x + x, from.y + y);
}
int distanceToEncodingCount(float distance_mm) {
const float distance_per_encoded = 10.21;
return int(distance_mm / distance_per_encoded);
}
int angleToEncodingCount(float angle_rad) {
const float rad_per_encoded = 0.157;
return int(angle_rad / rad_per_encoded);
}
Robot::Robot(const std::string & hostIpAddress, uint16_t tcpPort, ILocationProvider* loc_tracker)
: _jsonRpcTcpClient(hostIpAddress, tcpPort) : _jsonRpcTcpClient(hostIpAddress, tcpPort)
, _irProximitysDistanceDetected(this) , _irProximitysDistanceDetected(this)
, _lineTracksIsDetected(this, 1, false) , _lineTracksIsDetected(this, 1, false)
@ -14,6 +38,7 @@ Robot::Robot(const std::string & hostIpAddress, uint16_t tcpPort)
, _isReadySemaphore(0) , _isReadySemaphore(0)
, _eventCvMutex() , _eventCvMutex()
, _eventCv() , _eventCv()
, _loc_tracker(loc_tracker)
{ {
_jsonRpcTcpClient.bindNotification("irProximityDistanceDetected", [this](const Json::Value & params){ _jsonRpcTcpClient.bindNotification("irProximityDistanceDetected", [this](const Json::Value & params){
std::size_t index = params["index"].asUInt(); std::size_t index = params["index"].asUInt();
@ -28,8 +53,10 @@ Robot::Robot(const std::string & hostIpAddress, uint16_t tcpPort)
_lineTracksValue.set(index, params["value"].asUInt(), params["changedCount"].asInt()); _lineTracksValue.set(index, params["value"].asUInt(), params["changedCount"].asInt());
}); });
_jsonRpcTcpClient.bindNotification("encoderWheelValue", [this](const Json::Value & params){ _jsonRpcTcpClient.bindNotification("encoderWheelValue", [this](const Json::Value & params){
// this->_loc_tracker->progress();
std::size_t index = params["index"].asUInt(); std::size_t index = params["index"].asUInt();
_encoderWheelsValue.set(index, params["value"].asUInt(), params["changedCount"].asInt()); auto steps = params["changedCount"].asInt();
_encoderWheelsValue.set(index, params["value"].asUInt(), steps);
}); });
_jsonRpcTcpClient.bindNotification("switchIsDetected", [this](const Json::Value & params){ _jsonRpcTcpClient.bindNotification("switchIsDetected", [this](const Json::Value & params){
std::size_t index = params["index"].asUInt(); std::size_t index = params["index"].asUInt();
@ -57,6 +84,7 @@ void Robot::setMotorPower(MotorIndex motorIndex, float value)
_jsonRpcTcpClient.callNotification("setMotorPower", params); _jsonRpcTcpClient.callNotification("setMotorPower", params);
} }
void Robot::setMotorsPower(float rightValue, float leftValue) void Robot::setMotorsPower(float rightValue, float leftValue)
{ {
Json::Value params; Json::Value params;
@ -65,6 +93,37 @@ void Robot::setMotorsPower(float rightValue, float leftValue)
_jsonRpcTcpClient.callNotification("setMotorsPower", params); _jsonRpcTcpClient.callNotification("setMotorsPower", params);
} }
void Robot::doStraightLine(float distance_mm, float power) {
Json::Value params;
auto encodingSteps = distanceToEncodingCount(distance_mm);
params["steps"] = encodingSteps;
params["power"] = power;
// DBG(_loc_tracker->get_pos().x);
// // DBG(_loc_tracker->get_pos().y);
// auto pos = move_from(_loc_tracker->get_pos(), _loc_tracker->get_rot(), distance_mm);
// // DBG(pos.x);
// // DBG(pos.y);
// _loc_tracker->start(pos, _loc_tracker->get_rot(), encodingSteps * 2);
_jsonRpcTcpClient.callNotification("straightLine", params);
DBG("doStraightLine");
DBG(encodingSteps);
_loc_tracker->hint_left(true);
_loc_tracker->hint_right(true);
}
void Robot::doRotate(float angle_rad, float power) {
Json::Value params;
auto encodingSteps = angleToEncodingCount(angle_rad);
params["steps"] = encodingSteps;
params["power"] = power;
// _loc_tracker->start(_loc_tracker->get_pos(), _loc_tracker->get_rot() + angle_rad, encodingSteps * 2);
_jsonRpcTcpClient.callNotification("rotate", params);
DBG("doRotate");
DBG(encodingSteps);
_loc_tracker->hint_left(false);
_loc_tracker->hint_right(true);
}
void Robot::waitChanged(EventType eventType) { void Robot::waitChanged(EventType eventType) {
auto eventTypes = waitParamHelper({eventType}); auto eventTypes = waitParamHelper({eventType});
waitChangedHelper(eventTypes); waitChangedHelper(eventTypes);

View file

@ -1,6 +1,7 @@
#ifndef ROBOT_HPP #ifndef ROBOT_HPP
#define ROBOT_HPP #define ROBOT_HPP
#include "SimpleLocTracker.hpp"
#include "jsonrpctcpclient.hpp" #include "jsonrpctcpclient.hpp"
#include "values.hpp" #include "values.hpp"
@ -24,8 +25,9 @@ enum class EventType
class Robot : public IRobot<EventType> class Robot : public IRobot<EventType>
{ {
public: public:
ILocationProvider* _loc_tracker;
//! @brief Create a new robot connexion with a robot server (simu or reel). //! @brief Create a new robot connexion with a robot server (simu or reel).
Robot(const std::string & hostIpAddress, uint16_t tcpPort); Robot(const std::string & hostIpAddress, uint16_t tcpPort, ILocationProvider* loc_tracker);
//! @brief Close the robot connexion. //! @brief Close the robot connexion.
virtual ~Robot(); virtual ~Robot();
@ -66,6 +68,9 @@ public:
void setMotorsPower(float rightValue, float leftValue); void setMotorsPower(float rightValue, float leftValue);
//! \} //! \}
void doStraightLine(float distance_mm, float power = 0.25);
void doRotate(float angle_rad, float power = 0.25);
//! @brief Wait until this specific event has been received //! @brief Wait until this specific event has been received
void waitChanged(EventType eventType); void waitChanged(EventType eventType);

40
src/simpleLocTracker.cpp Normal file
View file

@ -0,0 +1,40 @@
#include "SimpleLocTracker.hpp"
#include <SFML/System/Vector2.hpp>
void SimpleLocTracker::start(sf::Vector2f to_pos, float to_rot, int total_encodings) {
last_pos = get_pos();
last_rot = get_rot();
current_mission = Mission {
.from_pos = last_pos, .to_pos = to_pos,
.from_rot = last_rot, .to_rot = to_rot,
.total_encodings = total_encodings, .current_encodings = 0,
};
}
void SimpleLocTracker::progress() {
if (!current_mission.has_value()) return;
current_mission.value().current_encodings += 1;
}
inline float interpolate(float from, float to, float fac) {
auto inv = 1. - fac;
return from * inv + to * fac;
}
inline sf::Vector2f interpolate(sf::Vector2f from, sf::Vector2f to, float fac) {
return sf::Vector2f(
interpolate(from.x, to.x, fac),
interpolate(from.y, to.y, fac)
);
}
sf::Vector2f SimpleLocTracker::get_pos() {
if (!current_mission.has_value()) return last_pos;
return interpolate(current_mission.value().from_pos, current_mission.value().to_pos, current_mission.value().progress());
}
float SimpleLocTracker::get_rot() {
if (!current_mission.has_value()) return last_rot;
return interpolate(current_mission.value().from_rot, current_mission.value().to_rot, current_mission.value().progress());
}

View file

@ -1,5 +1,63 @@
#pragma once #pragma once
#include <iostream> #include <iostream>
#include <SFML/System/Vector2.hpp>
#include <cmath>
#include <concepts>
#include <numbers>
#define DBG(X) ({ auto value = X; std::cout << "(DBG) [" << __FILE__ << ":" << __LINE__ << "] (" << #X << ") = " << value << "\n"; value;}) #define DBG(X) ({ auto value = X; std::cout << "(DBG) [" << __FILE__ << ":" << __LINE__ << "] (" << #X << ") = " << value << "\n"; value;})
inline namespace utils {
template <typename T>
concept is_numeric= std::integral<T> || std::floating_point<T>;
template<typename T> requires(is_numeric<T>)
class RangeIterator {
public:
inline RangeIterator(T index, T increment): _index(index), _increment(increment) {}
const inline T operator*() { return _index; }
const inline RangeIterator operator++() { _index += _increment; return *this; }
// note : incorrect comparison needed to support saturating iterations.
inline friend bool operator== (const RangeIterator<T>& start, const RangeIterator<T>& end) { return start._index >= end._index; }
inline friend bool operator!= (const RangeIterator<T>& start, const RangeIterator<T>& end) { return !(start._index >= end._index); }
private:
T _index;
T _increment;
};
template<typename T> requires(is_numeric<T>)
class Range {
public:
inline Range(T to) : _from(0), _to(to), _increment(1) {};
inline Range(T from, T to) : _from(from), _to(to), _increment(1) {};
inline Range(T from, T to, T increment) : _from(from), _to(to), _increment(increment) {};
const inline RangeIterator<T> begin() {
return RangeIterator(_from, _increment);
}
const inline RangeIterator<T> end() {
return RangeIterator(_to, _increment);
}
private:
T _from;
T _to;
T _increment;
};
template<typename T> requires(is_numeric<T>)
inline T vectorLength(const sf::Vector2<T>& v) {
return std::sqrt(v.x * v.x + v.y * v.y);
}
inline float deg_to_rad(float deg) { return (deg / 360) * (2 * std::numbers::pi); }
inline float rad_to_deg(float rad) {
return (rad / (2 * std::numbers::pi)) * (360);
}
inline sf::Vector2f scaled(sf::Vector2f vec, float fac) { return sf::Vector2f(vec.x * fac, vec.y * fac); }
}