From 749edf40d76bc68cef463732f850a3227c572623 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Sun, 16 Jun 2024 18:59:49 -0700 Subject: [PATCH 01/19] feat: :sparkles: add drive curve implementation Add an abstarct DriveCurve class and a concrete ExproDriveCurve class, both based on LemLib's implementation. --- include/gamepad/drive_curve.hpp | 77 +++++++++++++++++++++++++++++++++ src/gamepad/drive_curve.cpp | 23 ++++++++++ 2 files changed, 100 insertions(+) create mode 100644 include/gamepad/drive_curve.hpp create mode 100644 src/gamepad/drive_curve.cpp diff --git a/include/gamepad/drive_curve.hpp b/include/gamepad/drive_curve.hpp new file mode 100644 index 0000000..96182d6 --- /dev/null +++ b/include/gamepad/drive_curve.hpp @@ -0,0 +1,77 @@ +#pragma once + +namespace Gamepad { + +/** + * @brief abstract DriveCurve class + * + * This is an abstract class to enable users to provide their own, custom drive + * curves for LemLib to use. + */ +class DriveCurve { + public: + /** + * @brief Processes an input and returns an output + * + * This is a pure virtual function that needs to be overriden by child classes + * + * @param input the input to process + * @return float output + */ + virtual float curve(float input) = 0; +}; + +/** + * @brief ExpoDriveCurve class. Inherits from the DriveCurve class. This is a exponential drive curve. + * + * see https://www.desmos.com/calculator/umicbymbnl for an interactive graph + * see https://www.vexforum.com/t/expo-drive-lemlibs-implementation for a detailed explanation + */ +class ExpoDriveCurve : public DriveCurve { + public: + /** + * @brief Construct a new Expo Drive Curve object + * + * see https://www.desmos.com/calculator/umicbymbnl for an interactive graph + * see https://www.vexforum.com/t/expo-drive-lemlibs-implementation for a detailed explanation + * + * @param deadband range where input is considered to be input + * @param minOutput the minimum output that can be returned + * @param curve how "curved" the graph is + * + * @b Example + * @code {.cpp} + * // create a new exponential drive curve + * // controller deadband is set to 5 + * // minimum output is set to 12 + * // curve gain is set to 1.132 + * lemlib::ExpoDriveCurve driveCurve(5, 12, 1.132); + * @endcode + */ + ExpoDriveCurve(float deadband, float minOutput, float curve); + /** + * @brief curve an input + * + * @param input the input to curve + * @return float the curved output + * + * @b Example + * @code {.cpp} + * void opcontrol() { + * // create a new exponential drive curve + * lemlib::ExpoDriveCurve driveCurve(5, 12, 1.132); + * // input 4 + * std::cout << driveCurve.curve(4) << std::endl; // outputs 0 because input is within deadzone + * // input 6 + * std::cout << driveCurve.curve(6) << std::endl; // outputs 12, the minimum output + * std::cout << driveCurve.curve(127) << std::endl; // outputs 127, the maximum output + * } + * @endcode + */ + float curve(float input); + private: + const float deadband = 0; + const float minOutput = 0; + const float curveGain = 1; +}; +} // namespace Gamepad \ No newline at end of file diff --git a/src/gamepad/drive_curve.cpp b/src/gamepad/drive_curve.cpp new file mode 100644 index 0000000..61ece3f --- /dev/null +++ b/src/gamepad/drive_curve.cpp @@ -0,0 +1,23 @@ +#include "gamepad/drive_curve.hpp" +#include + +namespace Gamepad { +ExpoDriveCurve::ExpoDriveCurve(float deadband, float minOutput, float curve) + : deadband(deadband), + minOutput(minOutput), + curveGain(curve) {} + +float ExpoDriveCurve::curve(float input) { + // return 0 if input is within deadzone + if (std::abs(input) <= deadband) return 0; + // g is the output of g(x) as defined in the Desmos graph + const float g = std::abs(input) - deadband; + // g127 is the output of g(127) as defined in the Desmos graph + const float g127 = 127 - deadband; + // i is the output of i(x) as defined in the Desmos graph + const float i = std::copysign(std::pow(curveGain, g - 127) * g, input); + // i127 is the output of i(127) as defined in the Desmos graph + const float i127 = std::pow(curveGain, g127 - 127) * g127; + return (127.0 - minOutput) / (127) * i * 127 / i127 + std::copysign(minOutput, input); +} +} // namespace Gamepad \ No newline at end of file From 1ca81b01eb5896eb519a442e71cad99b83c825fb Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:24:44 -0700 Subject: [PATCH 02/19] feat: :sparkles: Add joystick curves to controller Allow users to assign a DriveCurve per axis to be used by the controller when updating joystick values. --- include/gamepad/controller.hpp | 57 +++++++++++++++++++--------------- src/gamepad/controller.cpp | 46 +++++++++++++++------------ 2 files changed, 59 insertions(+), 44 deletions(-) diff --git a/include/gamepad/controller.hpp b/include/gamepad/controller.hpp index 2c520e0..9484156 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/controller.hpp @@ -2,11 +2,13 @@ #include #include +#include #ifndef PROS_USE_SIMPLE_NAMES #define PROS_USE_SIMPLE_NAMES #endif #include "event_handler.hpp" +#include "drive_curve.hpp" #include "pros/misc.hpp" #include "pros/rtos.hpp" @@ -19,34 +21,35 @@ enum EventType { }; class Button { - friend class Controller; + friend class Controller; public: - bool rising_edge = false; - bool falling_edge = false; - bool is_pressed = false; - uint32_t last_press_time = pros::millis(); - uint32_t last_release_time = last_press_time; - uint32_t time_held = 0; - uint32_t time_released = 0; - uint32_t long_press_threshold = 500; + bool rising_edge = false; + bool falling_edge = false; + bool is_pressed = false; + uint32_t last_press_time = pros::millis(); + uint32_t last_release_time = last_press_time; + uint32_t time_held = 0; + uint32_t time_released = 0; + uint32_t long_press_threshold = 500; - uint32_t onPress(std::function func); - uint32_t onLongPress(std::function func); - uint32_t onRelease(std::function func); - uint32_t addListener(EventType event, std::function func); - bool removeListener(uint32_t id); + uint32_t onPress(std::function func); + uint32_t onLongPress(std::function func); + uint32_t onRelease(std::function func); + uint32_t addListener(EventType event, std::function func); + bool removeListener(uint32_t id); private: + void update(bool is_held); - void update(bool is_held); - - EventHandler<> onPressEvent; - EventHandler<> onLongPressEvent; - EventHandler<> onReleaseEvent; + EventHandler<> onPressEvent; + EventHandler<> onLongPressEvent; + EventHandler<> onReleaseEvent; }; class Controller { public: - explicit Controller(pros::controller_id_e_t id): controller(id) {} + explicit Controller(pros::controller_id_e_t id) + : controller(id) {} + /** * Updates the state of the gamepad (all joysticks and buttons), and also runs * any registered handlers. @@ -64,14 +67,18 @@ class Controller { * @param joystick Which joystick axis's value to return */ float operator[](pros::controller_analog_e_t joystick); + void setCurve(pros::controller_analog_e_t joystick, std::shared_ptr curve); + void setCurve(pros::controller_analog_e_t joystick, DriveCurve& curve); TODO("hide memebrs and expose getters/const refs") - Button L1{}, L2{}, R1{}, R2{}, - Up{}, Down{}, Left{}, Right{}, - X{}, B{}, Y{}, A{}; + Button L1 {}, L2 {}, R1 {}, R2 {}, Up {}, Down {}, Left {}, Right {}, X {}, B {}, Y {}, A {}; float LeftX = 0, LeftY = 0, RightX = 0, RightY = 0; private: - static Button Controller::* button_to_ptr(pros::controller_digital_e_t button); + std::shared_ptr CurveLeftX {nullptr}; + std::shared_ptr CurveLeftY {nullptr}; + std::shared_ptr CurveRightX {nullptr}; + std::shared_ptr CurveRightY {nullptr}; + static Button Controller::*button_to_ptr(pros::controller_digital_e_t button); void updateButton(pros::controller_digital_e_t button_id); pros::Controller controller; }; // namespace Gamepad -} \ No newline at end of file +} // namespace Gamepad \ No newline at end of file diff --git a/src/gamepad/controller.cpp b/src/gamepad/controller.cpp index 4472527..80d2657 100644 --- a/src/gamepad/controller.cpp +++ b/src/gamepad/controller.cpp @@ -3,9 +3,7 @@ namespace Gamepad { -uint32_t Button::onPress(std::function func) { - return this->onPressEvent.add_listener(std::move(func)); -} +uint32_t Button::onPress(std::function func) { return this->onPressEvent.add_listener(std::move(func)); } uint32_t Button::onLongPress(std::function func) { return this->onLongPressEvent.add_listener(std::move(func)); @@ -26,12 +24,8 @@ void Button::update(const bool is_held) { } else { this->time_released += pros::millis() - last_update_time; } - if (this->rising_edge) { - this->time_held = 0; - } - if (this->falling_edge) { - this->time_released = 0; - } + if (this->rising_edge) { this->time_held = 0; } + if (this->falling_edge) { this->time_released = 0; } if (this->rising_edge) { onPressEvent.fire(); @@ -43,20 +37,22 @@ void Button::update(const bool is_held) { } void Controller::updateButton(pros::controller_digital_e_t button_id) { - Button Controller::* button = Controller::button_to_ptr(button_id); + Button Controller::*button = Controller::button_to_ptr(button_id); bool is_held = this->controller.get_digital(button_id); (this->*button).update(is_held); } void Controller::update() { - for(int i = DIGITAL_L1; i != DIGITAL_A; ++i) { - this->updateButton(static_cast(i)); - } + for (int i = DIGITAL_L1; i != DIGITAL_A; ++i) { this->updateButton(static_cast(i)); } this->LeftX = this->controller.get_analog(ANALOG_LEFT_X); + if (this->CurveLeftX) this->LeftX = this->CurveLeftX->curve(this->LeftX); this->LeftY = this->controller.get_analog(ANALOG_LEFT_Y); + if (this->CurveLeftY) this->LeftY = this->CurveLeftY->curve(this->LeftY); this->RightX = this->controller.get_analog(ANALOG_RIGHT_X); + if (this->CurveRightX) this->RightX = this->CurveRightX->curve(this->RightX); this->RightY = this->controller.get_analog(ANALOG_RIGHT_Y); + if (this->CurveRightY) this->RightY = this->CurveRightY->curve(this->RightY); } const Button& Controller::operator[](pros::controller_digital_e_t button) { @@ -68,13 +64,26 @@ float Controller::operator[](pros::controller_analog_e_t axis) { case ANALOG_LEFT_X: return this->LeftX; case ANALOG_LEFT_Y: return this->LeftY; case ANALOG_RIGHT_X: return this->RightX; - case ANALOG_RIGHT_Y: return this->RightY; - TODO("change handling for default") + case ANALOG_RIGHT_Y: return this->RightY; TODO("change handling for default") default: std::exit(1); } } -Button Controller::* Controller::button_to_ptr(pros::controller_digital_e_t button) { +void Controller::setCurve(pros::controller_analog_e_t joystick, std::shared_ptr curve) { + switch (axis) { + case ANALOG_LEFT_X: this->CurveLeftX = curve; + case ANALOG_LEFT_Y: this->CurveLeftY = curve; + case ANALOG_RIGHT_X: this->CurveRightX = curve; + case ANALOG_RIGHT_Y: this->CurveRightY = curve; TODO("add error logging") + default: errno = EINVAL; + } +} + +void Controller::setCurve(pros::controller_analog_e_t joystick, DriveCurve& curve) { + this->setCurve(joystick, std::shared_ptr(&curve, [](void*) {})); +} + +Button Controller::*Controller::button_to_ptr(pros::controller_digital_e_t button) { switch (button) { case DIGITAL_L1: return &Controller::L1; case DIGITAL_L2: return &Controller::L2; @@ -87,9 +96,8 @@ Button Controller::* Controller::button_to_ptr(pros::controller_digital_e_t butt case DIGITAL_X: return &Controller::X; case DIGITAL_B: return &Controller::B; case DIGITAL_Y: return &Controller::Y; - case DIGITAL_A: return &Controller::A; - TODO("change handling for default") + case DIGITAL_A: return &Controller::A; TODO("change handling for default") default: std::exit(1); } } -} \ No newline at end of file +} // namespace Gamepad \ No newline at end of file From 0772340a65c33d36074cb890a5a83e3de95ab5fe Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:25:12 -0700 Subject: [PATCH 03/19] style: :art: Format event_handler.hpp --- include/gamepad/event_handler.hpp | 70 ++++++++++++++++--------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/include/gamepad/event_handler.hpp b/include/gamepad/event_handler.hpp index 6bdc188..d4660a0 100644 --- a/include/gamepad/event_handler.hpp +++ b/include/gamepad/event_handler.hpp @@ -12,44 +12,46 @@ namespace Gamepad { class MonotonicCounter { - template friend class EventHandler; - static uint32_t next_value() { - static std::atomic counter = 0; - return ++counter; - } + template friend class EventHandler; + + static uint32_t next_value() { + static std::atomic counter = 0; + return ++counter; + } }; -template -class EventHandler { +template class EventHandler { public: - using Listener = std::function; - uint32_t add_listener(Listener func) { - std::lock_guard lock(mutex); - uint32_t id = MonotonicCounter::next_value(); - listeners.emplace(id, std::move(func)); - return id; - } - bool remove_listener(uint32_t id) { - std::lock_guard lock(mutex); - if(listeners.find(id) == listeners.end()) { - TODO("change handling maybe?") - return false; + using Listener = std::function; + + uint32_t add_listener(Listener func) { + std::lock_guard lock(mutex); + uint32_t id = MonotonicCounter::next_value(); + listeners.emplace(id, std::move(func)); + return id; + } + + bool remove_listener(uint32_t id) { + std::lock_guard lock(mutex); + if (listeners.find(id) == listeners.end()) { + TODO("change handling maybe?") + return false; + } + listeners.erase(id); + return true; + } + + bool is_empty() { + std::lock_guard lock(mutex); + return listeners.empty(); } - listeners.erase(id); - return true; - } - bool is_empty() { - std::lock_guard lock(mutex); - return listeners.empty(); - } - void fire(Args... args) { - std::lock_guard lock(mutex); - for(auto listener : listeners) { - listener.second(args...); + + void fire(Args... args) { + std::lock_guard lock(mutex); + for (auto listener : listeners) { listener.second(args...); } } - } private: - std::map listeners; - pros::Mutex mutex; + std::map listeners; + pros::Mutex mutex; }; -} \ No newline at end of file +} // namespace Gamepad \ No newline at end of file From 846ba1a62518b99d8c0be0d7366c42bf4e110696 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:49:37 -0700 Subject: [PATCH 04/19] fix: :bug: Fix variable name typo --- src/gamepad/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gamepad/controller.cpp b/src/gamepad/controller.cpp index 80d2657..8a7fb3c 100644 --- a/src/gamepad/controller.cpp +++ b/src/gamepad/controller.cpp @@ -69,7 +69,7 @@ float Controller::operator[](pros::controller_analog_e_t axis) { } } -void Controller::setCurve(pros::controller_analog_e_t joystick, std::shared_ptr curve) { +void Controller::setCurve(pros::controller_analog_e_t axis, std::shared_ptr curve) { switch (axis) { case ANALOG_LEFT_X: this->CurveLeftX = curve; case ANALOG_LEFT_Y: this->CurveLeftY = curve; From 734183b3c144afa159a8d0b529f7301403ee2d12 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Tue, 25 Jun 2024 11:18:52 -0700 Subject: [PATCH 05/19] refactor: :construction: Remove deadband from curve --- include/gamepad/drive_curve.hpp | 4 +--- src/gamepad/drive_curve.cpp | 13 +++---------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/include/gamepad/drive_curve.hpp b/include/gamepad/drive_curve.hpp index 96182d6..8d9c80a 100644 --- a/include/gamepad/drive_curve.hpp +++ b/include/gamepad/drive_curve.hpp @@ -35,7 +35,6 @@ class ExpoDriveCurve : public DriveCurve { * see https://www.desmos.com/calculator/umicbymbnl for an interactive graph * see https://www.vexforum.com/t/expo-drive-lemlibs-implementation for a detailed explanation * - * @param deadband range where input is considered to be input * @param minOutput the minimum output that can be returned * @param curve how "curved" the graph is * @@ -48,7 +47,7 @@ class ExpoDriveCurve : public DriveCurve { * lemlib::ExpoDriveCurve driveCurve(5, 12, 1.132); * @endcode */ - ExpoDriveCurve(float deadband, float minOutput, float curve); + ExpoDriveCurve(float minOutput, float curve); /** * @brief curve an input * @@ -70,7 +69,6 @@ class ExpoDriveCurve : public DriveCurve { */ float curve(float input); private: - const float deadband = 0; const float minOutput = 0; const float curveGain = 1; }; diff --git a/src/gamepad/drive_curve.cpp b/src/gamepad/drive_curve.cpp index 61ece3f..2d4f17a 100644 --- a/src/gamepad/drive_curve.cpp +++ b/src/gamepad/drive_curve.cpp @@ -2,22 +2,15 @@ #include namespace Gamepad { -ExpoDriveCurve::ExpoDriveCurve(float deadband, float minOutput, float curve) - : deadband(deadband), +ExpoDriveCurve::ExpoDriveCurve(float minOutput, float curve) : minOutput(minOutput), curveGain(curve) {} float ExpoDriveCurve::curve(float input) { - // return 0 if input is within deadzone - if (std::abs(input) <= deadband) return 0; // g is the output of g(x) as defined in the Desmos graph - const float g = std::abs(input) - deadband; - // g127 is the output of g(127) as defined in the Desmos graph - const float g127 = 127 - deadband; + const float g = std::abs(input); // i is the output of i(x) as defined in the Desmos graph const float i = std::copysign(std::pow(curveGain, g - 127) * g, input); - // i127 is the output of i(127) as defined in the Desmos graph - const float i127 = std::pow(curveGain, g127 - 127) * g127; - return (127.0 - minOutput) / (127) * i * 127 / i127 + std::copysign(minOutput, input); + return (127.0 - minOutput) / (127) * i + std::copysign(minOutput, input); } } // namespace Gamepad \ No newline at end of file From ae8555444c81456c1e2c503cc8df5bcb0c00bcaa Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:29:15 -0700 Subject: [PATCH 06/19] fix: :pencil2: remove merge conflict markers --- include/gamepad/event_handler.hpp | 45 ------------------------------- 1 file changed, 45 deletions(-) diff --git a/include/gamepad/event_handler.hpp b/include/gamepad/event_handler.hpp index 77706a6..bf51b20 100644 --- a/include/gamepad/event_handler.hpp +++ b/include/gamepad/event_handler.hpp @@ -9,38 +9,6 @@ namespace gamepad::_impl { -<<<<<<< HEAD -class MonotonicCounter { - template friend class EventHandler; - - static uint32_t next_value() { - static std::atomic counter = 0; - return ++counter; - } -}; - -template class EventHandler { - public: - using Listener = std::function; - - uint32_t add_listener(Listener func) { - std::lock_guard lock(mutex); - uint32_t id = MonotonicCounter::next_value(); - listeners.emplace(id, std::move(func)); - return id; - } - - bool remove_listener(uint32_t id) { - std::lock_guard lock(mutex); - if (listeners.find(id) == listeners.end()) { - TODO("change handling maybe?") - return false; - } - listeners.erase(id); - return true; - } - -======= /** * @brief Event handling class with thread safety that supports adding, removing, and running listeners * @@ -91,23 +59,11 @@ template class EventHandler { * @return true There are listeners registered * @return false There are no listeners registered */ ->>>>>>> b19f6224e83dcbbf6f3b02059fa6d64b57af4ac8 bool is_empty() { std::lock_guard lock(mutex); return listeners.empty(); } -<<<<<<< HEAD - void fire(Args... args) { - std::lock_guard lock(mutex); - for (auto listener : listeners) { listener.second(args...); } - } - private: - std::map listeners; - pros::Mutex mutex; -}; -} // namespace Gamepad -======= /** * @brief Runs each listener registered * @@ -123,4 +79,3 @@ template class EventHandler { gamepad::_impl::RecursiveMutex mutex {}; }; } // namespace gamepad::_impl ->>>>>>> b19f6224e83dcbbf6f3b02059fa6d64b57af4ac8 From 982c53b66ceabb84c4e16b83159559247ed9c50e Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Tue, 29 Oct 2024 18:29:15 +0000 Subject: [PATCH 07/19] fix: :bug: Fix compile errors from merging --- include/gamepad/controller.hpp | 1 + include/gamepad/drive_curve.hpp | 4 ++-- src/gamepad/controller.cpp | 16 ++++++++-------- src/gamepad/drive_curve.cpp | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/gamepad/controller.hpp b/include/gamepad/controller.hpp index be76313..5716083 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/controller.hpp @@ -4,6 +4,7 @@ #include #include #include "button.hpp" +#include "drive_curve.hpp" #include "pros/misc.hpp" namespace gamepad { diff --git a/include/gamepad/drive_curve.hpp b/include/gamepad/drive_curve.hpp index 8d9c80a..26e5e7e 100644 --- a/include/gamepad/drive_curve.hpp +++ b/include/gamepad/drive_curve.hpp @@ -1,6 +1,6 @@ #pragma once -namespace Gamepad { +namespace gamepad { /** * @brief abstract DriveCurve class @@ -72,4 +72,4 @@ class ExpoDriveCurve : public DriveCurve { const float minOutput = 0; const float curveGain = 1; }; -} // namespace Gamepad \ No newline at end of file +} // namespace gamepad \ No newline at end of file diff --git a/src/gamepad/controller.cpp b/src/gamepad/controller.cpp index c33a913..acf770b 100644 --- a/src/gamepad/controller.cpp +++ b/src/gamepad/controller.cpp @@ -17,13 +17,13 @@ void Gamepad::update() { } this->m_LeftX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_X); - if (this->CurveLeftX) this->LeftX = this->CurveLeftX->curve(this->LeftX); + if (this->CurveLeftX) this->m_LeftX = this->CurveLeftX->curve(this->LeftX); this->m_LeftY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y); - if (this->CurveLeftY) this->LeftY = this->CurveLeftY->curve(this->LeftY); + if (this->CurveLeftY) this->m_LeftY = this->CurveLeftY->curve(this->LeftY); this->m_RightX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_X); - if (this->CurveRightX) this->RightX = this->CurveRightX->curve(this->RightX); + if (this->CurveRightX) this->m_RightX = this->CurveRightX->curve(this->RightX); this->m_RightY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y); - if (this->CurveRightY) this->RightY = this->CurveRightY->curve(this->RightY); + if (this->CurveRightY) this->m_RightY = this->CurveRightY->curve(this->RightY); } const Button& Gamepad::operator[](pros::controller_digital_e_t button) { return this->*Gamepad::button_to_ptr(button); } @@ -48,10 +48,10 @@ std::string Gamepad::unique_name() { void Gamepad::setCurve(pros::controller_analog_e_t axis, std::shared_ptr curve) { switch (axis) { - case ANALOG_LEFT_X: this->CurveLeftX = curve; - case ANALOG_LEFT_Y: this->CurveLeftY = curve; - case ANALOG_RIGHT_X: this->CurveRightX = curve; - case ANALOG_RIGHT_Y: this->CurveRightY = curve; + case pros::E_CONTROLLER_ANALOG_LEFT_X: this->CurveLeftX = curve; + case pros::E_CONTROLLER_ANALOG_LEFT_Y: this->CurveLeftY = curve; + case pros::E_CONTROLLER_ANALOG_RIGHT_X: this->CurveRightX = curve; + case pros::E_CONTROLLER_ANALOG_RIGHT_Y: this->CurveRightY = curve; default: TODO("add error logging") errno = EINVAL; diff --git a/src/gamepad/drive_curve.cpp b/src/gamepad/drive_curve.cpp index 2d4f17a..dac1875 100644 --- a/src/gamepad/drive_curve.cpp +++ b/src/gamepad/drive_curve.cpp @@ -1,7 +1,7 @@ #include "gamepad/drive_curve.hpp" #include -namespace Gamepad { +namespace gamepad { ExpoDriveCurve::ExpoDriveCurve(float minOutput, float curve) : minOutput(minOutput), curveGain(curve) {} @@ -13,4 +13,4 @@ float ExpoDriveCurve::curve(float input) { const float i = std::copysign(std::pow(curveGain, g - 127) * g, input); return (127.0 - minOutput) / (127) * i + std::copysign(minOutput, input); } -} // namespace Gamepad \ No newline at end of file +} // namespace gamepad \ No newline at end of file From e7f749580c3620d63e3d4b40959ceed8289111d4 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Wed, 30 Oct 2024 18:53:10 +0000 Subject: [PATCH 08/19] refactor: :construction: Start work on joystick modifier classes --- include/gamepad/controller.hpp | 58 ++++++++++++--------- include/gamepad/drive_curve.hpp | 75 --------------------------- include/gamepad/joystick_modifier.hpp | 69 ++++++++++++++++++++++++ src/gamepad/controller.cpp | 28 ++-------- src/gamepad/drive_curve.cpp | 16 ------ src/gamepad/joystick_modifier.cpp | 35 +++++++++++++ src/main.cpp | 24 ++++----- 7 files changed, 154 insertions(+), 151 deletions(-) delete mode 100644 include/gamepad/drive_curve.hpp create mode 100644 include/gamepad/joystick_modifier.hpp delete mode 100644 src/gamepad/drive_curve.cpp create mode 100644 src/gamepad/joystick_modifier.cpp diff --git a/include/gamepad/controller.hpp b/include/gamepad/controller.hpp index 5716083..e44ae58 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/controller.hpp @@ -2,9 +2,8 @@ #include "pros/misc.h" #include -#include #include "button.hpp" -#include "drive_curve.hpp" +#include "joystick_modifier.hpp" #include "pros/misc.hpp" namespace gamepad { @@ -54,24 +53,39 @@ class Gamepad { * */ float operator[](pros::controller_analog_e_t joystick); - void setCurve(pros::controller_analog_e_t joystick, std::shared_ptr curve); - void setCurve(pros::controller_analog_e_t joystick, DriveCurve& curve); - const Button& L1 {m_L1}; - const Button& L2 {m_L2}; - const Button& R1 {m_R1}; - const Button& R2 {m_R2}; - const Button& Up {m_Up}; - const Button& Down {m_Down}; - const Button& Left {m_Left}; - const Button& Right {m_Right}; - const Button& X {m_X}; - const Button& B {m_B}; - const Button& Y {m_Y}; - const Button& A {m_A}; - const float& LeftX = m_LeftX; - const float& LeftY = m_LeftY; - const float& RightX = m_RightX; - const float& RightY = m_RightY; + + const Button& L1() { return m_L1; } + + const Button& L2() { return m_L2; } + + const Button& R1() { return m_R1; } + + const Button& R2() { return m_R2; } + + const Button& Up() { return m_Up; } + + const Button& Down() { return m_Down; } + + const Button& Left() { return m_Left; } + + const Button& Right() { return m_Right; } + + const Button& X() { return m_X; } + + const Button& B() { return m_B; } + + const Button& Y() { return m_Y; } + + const Button& A() { return m_A; } + + float LeftX() { return m_LeftX; } + + float LeftY() { return m_LeftY; } + + float RightX() { return m_RightX; } + + float RightY() { return m_RightY; } + /// The master controller, same as @ref gamepad::master static Gamepad master; /// The partner controller, same as @ref gamepad::partner @@ -83,10 +97,6 @@ class Gamepad { Button m_L1 {}, m_L2 {}, m_R1 {}, m_R2 {}, m_Up {}, m_Down {}, m_Left {}, m_Right {}, m_X {}, m_B {}, m_Y {}, m_A {}; float m_LeftX = 0, m_LeftY = 0, m_RightX = 0, m_RightY = 0; - std::shared_ptr CurveLeftX {nullptr}; - std::shared_ptr CurveLeftY {nullptr}; - std::shared_ptr CurveRightX {nullptr}; - std::shared_ptr CurveRightY {nullptr}; Button Fake {}; /** * @brief Gets a unique name for a listener that will not conflict with user listener names. diff --git a/include/gamepad/drive_curve.hpp b/include/gamepad/drive_curve.hpp deleted file mode 100644 index 26e5e7e..0000000 --- a/include/gamepad/drive_curve.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -namespace gamepad { - -/** - * @brief abstract DriveCurve class - * - * This is an abstract class to enable users to provide their own, custom drive - * curves for LemLib to use. - */ -class DriveCurve { - public: - /** - * @brief Processes an input and returns an output - * - * This is a pure virtual function that needs to be overriden by child classes - * - * @param input the input to process - * @return float output - */ - virtual float curve(float input) = 0; -}; - -/** - * @brief ExpoDriveCurve class. Inherits from the DriveCurve class. This is a exponential drive curve. - * - * see https://www.desmos.com/calculator/umicbymbnl for an interactive graph - * see https://www.vexforum.com/t/expo-drive-lemlibs-implementation for a detailed explanation - */ -class ExpoDriveCurve : public DriveCurve { - public: - /** - * @brief Construct a new Expo Drive Curve object - * - * see https://www.desmos.com/calculator/umicbymbnl for an interactive graph - * see https://www.vexforum.com/t/expo-drive-lemlibs-implementation for a detailed explanation - * - * @param minOutput the minimum output that can be returned - * @param curve how "curved" the graph is - * - * @b Example - * @code {.cpp} - * // create a new exponential drive curve - * // controller deadband is set to 5 - * // minimum output is set to 12 - * // curve gain is set to 1.132 - * lemlib::ExpoDriveCurve driveCurve(5, 12, 1.132); - * @endcode - */ - ExpoDriveCurve(float minOutput, float curve); - /** - * @brief curve an input - * - * @param input the input to curve - * @return float the curved output - * - * @b Example - * @code {.cpp} - * void opcontrol() { - * // create a new exponential drive curve - * lemlib::ExpoDriveCurve driveCurve(5, 12, 1.132); - * // input 4 - * std::cout << driveCurve.curve(4) << std::endl; // outputs 0 because input is within deadzone - * // input 6 - * std::cout << driveCurve.curve(6) << std::endl; // outputs 12, the minimum output - * std::cout << driveCurve.curve(127) << std::endl; // outputs 127, the maximum output - * } - * @endcode - */ - float curve(float input); - private: - const float minOutput = 0; - const float curveGain = 1; -}; -} // namespace gamepad \ No newline at end of file diff --git a/include/gamepad/joystick_modifier.hpp b/include/gamepad/joystick_modifier.hpp new file mode 100644 index 0000000..c3c6259 --- /dev/null +++ b/include/gamepad/joystick_modifier.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include +#include + +namespace gamepad { +class AbstractTransformation { + public: + virtual std::pair get_value(std::pair) = 0; + virtual ~AbstractTransformation() = default; +}; + +class Deadband : public AbstractTransformation { + public: + Deadband(float x_deadband, float y_deadband, float x_spread, float y_spread) + : m_x_deadband(x_deadband), + m_y_deadband(y_deadband), + m_x_spread(x_spread), + m_y_spread(y_spread) {} + + Deadband(float x_deadband, float y_deadband) + : Deadband(x_deadband, y_deadband, 0.0, 0.0) {} + + std::pair get_value(std::pair) override; + private: + float m_x_deadband; + float m_y_deadband; + float m_x_spread; + float m_y_spread; +}; + +class ExpoCurve : public AbstractTransformation { + public: + ExpoCurve(float x_curve, float y_curve) + : m_x_curve(x_curve), + m_y_curve(y_curve) {} + + std::pair get_value(std::pair) override; + private: + float m_x_curve; + float m_y_curve; +}; + +class Transformation final { + friend class TransformationBuilder; + public: + std::pair get_value(std::pair); + private: + std::vector> m_all_transforms; +}; + +class TransformationBuilder final { + public: + template T> TransformationBuilder(T first) { + m_transform.m_all_transforms.push_back(std::make_unique(std::move(first))); + } + + TransformationBuilder() = delete; + + template T> TransformationBuilder& and_then(T next) { + m_transform.m_all_transforms.push_back(std::make_unique(std::move(next))); + return *this; + } + private: + Transformation m_transform {}; +}; +} // namespace gamepad \ No newline at end of file diff --git a/src/gamepad/controller.cpp b/src/gamepad/controller.cpp index acf770b..354bc74 100644 --- a/src/gamepad/controller.cpp +++ b/src/gamepad/controller.cpp @@ -17,23 +17,19 @@ void Gamepad::update() { } this->m_LeftX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_X); - if (this->CurveLeftX) this->m_LeftX = this->CurveLeftX->curve(this->LeftX); this->m_LeftY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y); - if (this->CurveLeftY) this->m_LeftY = this->CurveLeftY->curve(this->LeftY); this->m_RightX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_X); - if (this->CurveRightX) this->m_RightX = this->CurveRightX->curve(this->RightX); this->m_RightY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y); - if (this->CurveRightY) this->m_RightY = this->CurveRightY->curve(this->RightY); } const Button& Gamepad::operator[](pros::controller_digital_e_t button) { return this->*Gamepad::button_to_ptr(button); } float Gamepad::operator[](pros::controller_analog_e_t axis) { switch (axis) { - case pros::E_CONTROLLER_ANALOG_LEFT_X: return this->LeftX; - case pros::E_CONTROLLER_ANALOG_LEFT_Y: return this->LeftY; - case pros::E_CONTROLLER_ANALOG_RIGHT_X: return this->RightX; - case pros::E_CONTROLLER_ANALOG_RIGHT_Y: return this->RightY; + case pros::E_CONTROLLER_ANALOG_LEFT_X: return m_LeftX; + case pros::E_CONTROLLER_ANALOG_LEFT_Y: return m_LeftY; + case pros::E_CONTROLLER_ANALOG_RIGHT_X: return m_RightX; + case pros::E_CONTROLLER_ANALOG_RIGHT_Y: return m_RightY; default: TODO("add error logging") errno = EINVAL; @@ -46,22 +42,6 @@ std::string Gamepad::unique_name() { return std::to_string(i++) + "_internal"; } -void Gamepad::setCurve(pros::controller_analog_e_t axis, std::shared_ptr curve) { - switch (axis) { - case pros::E_CONTROLLER_ANALOG_LEFT_X: this->CurveLeftX = curve; - case pros::E_CONTROLLER_ANALOG_LEFT_Y: this->CurveLeftY = curve; - case pros::E_CONTROLLER_ANALOG_RIGHT_X: this->CurveRightX = curve; - case pros::E_CONTROLLER_ANALOG_RIGHT_Y: this->CurveRightY = curve; - default: - TODO("add error logging") - errno = EINVAL; - } -} - -void Gamepad::setCurve(pros::controller_analog_e_t joystick, DriveCurve& curve) { - this->setCurve(joystick, std::shared_ptr(&curve, [](void*) {})); -} - Button Gamepad::*Gamepad::button_to_ptr(pros::controller_digital_e_t button) { switch (button) { case pros::E_CONTROLLER_DIGITAL_L1: return &Gamepad::m_L1; diff --git a/src/gamepad/drive_curve.cpp b/src/gamepad/drive_curve.cpp deleted file mode 100644 index dac1875..0000000 --- a/src/gamepad/drive_curve.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "gamepad/drive_curve.hpp" -#include - -namespace gamepad { -ExpoDriveCurve::ExpoDriveCurve(float minOutput, float curve) : - minOutput(minOutput), - curveGain(curve) {} - -float ExpoDriveCurve::curve(float input) { - // g is the output of g(x) as defined in the Desmos graph - const float g = std::abs(input); - // i is the output of i(x) as defined in the Desmos graph - const float i = std::copysign(std::pow(curveGain, g - 127) * g, input); - return (127.0 - minOutput) / (127) * i + std::copysign(minOutput, input); -} -} // namespace gamepad \ No newline at end of file diff --git a/src/gamepad/joystick_modifier.cpp b/src/gamepad/joystick_modifier.cpp new file mode 100644 index 0000000..1741acf --- /dev/null +++ b/src/gamepad/joystick_modifier.cpp @@ -0,0 +1,35 @@ +#include "joystick_modifier.hpp" +#include +#include + +using std::abs; +using std::copysign; +using std::pow; + +namespace gamepad { +std::pair Deadband::get_value(std::pair value) { + float x = value.first; + float y = value.second; + float x_deadband = m_x_deadband + abs(y) * m_x_spread; + float y_deadband = m_y_deadband + abs(x) * m_y_spread; + float x_scale = 1 - x_deadband; + float y_scale = 1 - y_deadband; + x = copysign(abs(x) < x_deadband ? 0 : (abs(x) - x_deadband) * x_scale, x); + y = copysign(abs(y) < y_deadband ? 0 : (abs(y) - y_deadband) * y_scale, y); + return {x, y}; +} + +std::pair ExpoCurve::get_value(std::pair value) { + float x = value.first; + float y = value.second; + x = copysign(pow(abs(x), m_x_curve), x); + x = copysign(pow(abs(y), m_y_curve), y); + return {x, y}; +} + +std::pair Transformation::get_value(std::pair value) { + std::pair ret_value {}; + return std::accumulate(m_all_transforms.begin(), m_all_transforms.end(), ret_value, + [](auto last_val, auto& next_transform) { return next_transform->get_value(last_val); }); +} +} // namespace gamepad \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b0bef76..8d2cc4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,22 +36,22 @@ void aRepeatPress1() { void initialize() { // We can register functions to run when buttons are pressed - gamepad::master.Down.onPress("downPress1", downPress1); + gamepad::master.Down().onPress("downPress1", downPress1); // ...or when they're released - gamepad::master.Up.onRelease("downRelease1", upRelease1); + gamepad::master.Up().onRelease("downRelease1", upRelease1); // There's also the longPress event - gamepad::master.Left.onLongPress("leftLongPress1", leftLongPress1); + gamepad::master.Left().onLongPress("leftLongPress1", leftLongPress1); // We can have two or even more functions on one button, // just remember to give them different names - gamepad::master.Left.onShortRelease("leftShortRelease", leftShortRelease1); - gamepad::master.Left.onLongRelease("leftLongRelease", leftLongRelease1); + gamepad::master.Left().onShortRelease("leftShortRelease", leftShortRelease1); + gamepad::master.Left().onLongRelease("leftLongRelease", leftLongRelease1); // We also have the repeat press event, where we can adjust the timing - gamepad::master.A.set_long_press_threshold(1000); // in ms - gamepad::master.A.set_repeat_cooldown(100); // in ms - gamepad::master.A.onPress("aStartPress", aPress1); - gamepad::master.A.onRepeatPress("aRepeatPress", aRepeatPress1); + gamepad::master.A().set_long_press_threshold(1000); // in ms + gamepad::master.A().set_repeat_cooldown(100); // in ms + gamepad::master.A().onPress("aStartPress", aPress1); + gamepad::master.A().onRepeatPress("aRepeatPress", aRepeatPress1); // And we can use lambda's too - gamepad::master.X.onShortRelease("xShortRelease1", []() { printf("X Short Release!\n"); }); + gamepad::master.X().onShortRelease("xShortRelease1", []() { printf("X Short Release!\n"); }); } /** @@ -106,8 +106,8 @@ void opcontrol() { // Remember to ALWAYS call update at the start of your while loop! gamepad::master.update(); // We'll use the arcade control scheme - int dir = gamepad::master.LeftY; // Gets amount forward/backward from left joystick - int turn = gamepad::master.RightX; // Gets the turn left/right from right joystick + int dir = gamepad::master.LeftY(); // Gets amount forward/backward from left joystick + int turn = gamepad::master.RightX(); // Gets the turn left/right from right joystick left_mg.move(dir - turn); // Sets left motor voltage right_mg.move(dir + turn); // Sets right motor voltage pros::delay(25); // Wait for 25 ms, then update the motor values again From 297295aecad553e98e1f4375a7d038e342387ad7 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Sat, 2 Nov 2024 11:41:36 -0700 Subject: [PATCH 09/19] feat: :sparkles: Add fisheye joystick modifier --- include/gamepad/joystick_modifier.hpp | 10 ++++++++++ src/gamepad/joystick_modifier.cpp | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/gamepad/joystick_modifier.hpp b/include/gamepad/joystick_modifier.hpp index c3c6259..6ccc509 100644 --- a/include/gamepad/joystick_modifier.hpp +++ b/include/gamepad/joystick_modifier.hpp @@ -43,6 +43,16 @@ class ExpoCurve : public AbstractTransformation { float m_y_curve; }; +class Fisheye : public AbstractTransformation { + public: + Fisheye(float radius) + : m_radius(radius) {} + + std::pair get_value(std::pair) override; + private: + float m_radius; +}; + class Transformation final { friend class TransformationBuilder; public: diff --git a/src/gamepad/joystick_modifier.cpp b/src/gamepad/joystick_modifier.cpp index 1741acf..be8258f 100644 --- a/src/gamepad/joystick_modifier.cpp +++ b/src/gamepad/joystick_modifier.cpp @@ -27,6 +27,22 @@ std::pair ExpoCurve::get_value(std::pair value) { return {x, y}; } +std::pair Fisheye::get_value(std::pair value) { + float x = value.first; + float y = value.second; + float x_abs = abs(x); + float y_abs = abs(y); + float j = std::sqrt(m_radius * m_radius - 127 * 127); + if (x_abs >= j && y_abs >= j) { + float theta = std::atan2(y_abs, x_abs); + x_abs *= std::acos(abs(std::remainder(theta, 90))); + y_abs *= std::acos(abs(std::remainder(theta, 90))); + } + x = std::copysign(std::min(127.0f, x_abs), x); + y = std::copysign(std::min(127.0f, y_abs), y); + return {x, y}; +} + std::pair Transformation::get_value(std::pair value) { std::pair ret_value {}; return std::accumulate(m_all_transforms.begin(), m_all_transforms.end(), ret_value, From 83c831573553bd2465e71d1b67176c79d32e90d6 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:36:57 -0800 Subject: [PATCH 10/19] feat: :construction: Add ability to add transformations to gamepad --- include/gamepad/controller.hpp | 33 +++++++++++++++++++++++---- include/gamepad/joystick_modifier.hpp | 12 ++++++++-- src/main.cpp | 5 ++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/include/gamepad/controller.hpp b/include/gamepad/controller.hpp index e44ae58..86140e9 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/controller.hpp @@ -1,6 +1,7 @@ #pragma once #include "pros/misc.h" +#include #include #include "button.hpp" #include "joystick_modifier.hpp" @@ -78,13 +79,35 @@ class Gamepad { const Button& A() { return m_A; } - float LeftX() { return m_LeftX; } + float LeftX(bool use_curve = true) { + if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).first; + else return m_LeftX; + } - float LeftY() { return m_LeftY; } + float LeftY(bool use_curve = true) { + if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).second; + else return m_LeftY; + } - float RightX() { return m_RightX; } + float RightX(bool use_curve = true) { + if (use_curve && m_right_transformation) + return m_right_transformation->get_value({m_RightX, m_RightY}).first; + else return m_RightX; + } - float RightY() { return m_RightY; } + float RightY(bool use_curve = true) { + if (use_curve && m_right_transformation) + return m_right_transformation->get_value({m_RightX, m_RightY}).second; + else return m_RightY; + } + + void set_left_transform(Transformation left_transformation) { + m_left_transformation = std::move(left_transformation); + } + + void set_right_transform(Transformation right_transformation) { + m_right_transformation = std::move(right_transformation); + } /// The master controller, same as @ref gamepad::master static Gamepad master; @@ -98,6 +121,8 @@ class Gamepad { m_A {}; float m_LeftX = 0, m_LeftY = 0, m_RightX = 0, m_RightY = 0; Button Fake {}; + std::optional m_left_transformation {std::nullopt}; + std::optional m_right_transformation {std::nullopt}; /** * @brief Gets a unique name for a listener that will not conflict with user listener names. * diff --git a/include/gamepad/joystick_modifier.hpp b/include/gamepad/joystick_modifier.hpp index 6ccc509..5819917 100644 --- a/include/gamepad/joystick_modifier.hpp +++ b/include/gamepad/joystick_modifier.hpp @@ -64,15 +64,23 @@ class Transformation final { class TransformationBuilder final { public: template T> TransformationBuilder(T first) { - m_transform.m_all_transforms.push_back(std::make_unique(std::move(first))); + m_transform.m_all_transforms.push_back(std::make_unique(std::move(first))); } TransformationBuilder() = delete; template T> TransformationBuilder& and_then(T next) { - m_transform.m_all_transforms.push_back(std::make_unique(std::move(next))); + m_transform.m_all_transforms.push_back(std::make_unique(std::move(next))); return *this; } + + Transformation build() { + return std::move(m_transform); + } + + operator Transformation() { + return std::move(m_transform); + } private: Transformation m_transform {}; }; diff --git a/src/main.cpp b/src/main.cpp index 8d2cc4a..dd04b8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include "main.h" #include "gamepad/api.hpp" #include "gamepad/controller.hpp" +#include "gamepad/joystick_modifier.hpp" #include "pros/rtos.hpp" #include #include @@ -52,6 +53,10 @@ void initialize() { gamepad::master.A().onRepeatPress("aRepeatPress", aRepeatPress1); // And we can use lambda's too gamepad::master.X().onShortRelease("xShortRelease1", []() { printf("X Short Release!\n"); }); + + // set up controller curves: + gamepad::master.set_left_transform( + gamepad::TransformationBuilder(gamepad::Deadband(5, 5)).and_then(gamepad::ExpoCurve(2, 2))); } /** From 67695e45ce269389b7b3f0f2af29905dc96b267b Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Mon, 4 Nov 2024 08:46:22 -0800 Subject: [PATCH 11/19] style: :art: Fix formatting in joystick_modifier.hpp --- include/gamepad/joystick_modifier.hpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/include/gamepad/joystick_modifier.hpp b/include/gamepad/joystick_modifier.hpp index 5819917..9eacd53 100644 --- a/include/gamepad/joystick_modifier.hpp +++ b/include/gamepad/joystick_modifier.hpp @@ -74,14 +74,10 @@ class TransformationBuilder final { return *this; } - Transformation build() { - return std::move(m_transform); - } + Transformation build() { return std::move(m_transform); } - operator Transformation() { - return std::move(m_transform); - } + operator Transformation() { return std::move(m_transform); } private: Transformation m_transform {}; }; -} // namespace gamepad \ No newline at end of file +} // namespace gamepad From fd99f2a1eea3753c4387e2888f2ef41bb98aea33 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Sat, 16 Nov 2024 15:43:57 -0800 Subject: [PATCH 12/19] docs: :memo: Add docs to Gamepad and Transformations --- include/gamepad/controller.hpp | 84 ++++++-- include/gamepad/joystick_modifier.hpp | 83 -------- include/gamepad/joystick_transformation.hpp | 188 ++++++++++++++++++ src/gamepad/controller.cpp | 8 +- ...difier.cpp => joystick_transformation.cpp} | 8 +- src/main.cpp | 27 ++- 6 files changed, 274 insertions(+), 124 deletions(-) delete mode 100644 include/gamepad/joystick_modifier.hpp create mode 100644 include/gamepad/joystick_transformation.hpp rename src/gamepad/{joystick_modifier.cpp => joystick_transformation.cpp} (89%) diff --git a/include/gamepad/controller.hpp b/include/gamepad/controller.hpp index 86140e9..7fb642a 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/controller.hpp @@ -4,7 +4,7 @@ #include #include #include "button.hpp" -#include "joystick_modifier.hpp" +#include "joystick_transformation.hpp" #include "pros/misc.hpp" namespace gamepad { @@ -46,65 +46,113 @@ class Gamepad { * * @param joystick Which joystick axis to return * + * @return float the value of the joystick, between -1.0 and 1.0. + * * @b Example: * @code {.cpp} * // control a motor with a joystick - * intake.move(gamepad::master[ANALOG_RIGHT_Y]); + * intake.move(gamepad::master[ANALOG_RIGHT_Y] * 127); * @endcode * */ float operator[](pros::controller_analog_e_t joystick); - const Button& L1() { return m_L1; } + /// The L1 button on the top of the controller. + const Button& buttonL1() { return m_L1; } - const Button& L2() { return m_L2; } + /// The L2 button on the top of the controller. + const Button& buttonL2() { return m_L2; } - const Button& R1() { return m_R1; } + /// The R1 button on the top of the controller. + const Button& buttonR1() { return m_R1; } - const Button& R2() { return m_R2; } + /// The R2 button on the top of the controller. + const Button& buttonR2() { return m_R2; } - const Button& Up() { return m_Up; } + /// The up arrow button on the front of the controller. + const Button& buttonUp() { return m_Up; } - const Button& Down() { return m_Down; } + /// The down arrow button on the front of the controller. + const Button& buttonDown() { return m_Down; } - const Button& Left() { return m_Left; } + /// The left arrow button on the front of the controller. + const Button& buttonLeft() { return m_Left; } - const Button& Right() { return m_Right; } + /// The right arrow button on the front of the controller. + const Button& buttonRight() { return m_Right; } - const Button& X() { return m_X; } + /// The X arrow button on the front of the controller. + const Button& buttonX() { return m_X; } - const Button& B() { return m_B; } + /// The B arrow button on the front of the controller. + const Button& buttonB() { return m_B; } - const Button& Y() { return m_Y; } + /// The Y arrow button on the front of the controller. + const Button& buttonY() { return m_Y; } - const Button& A() { return m_A; } + /// The A arrow button on the front of the controller. + const Button& buttonA() { return m_A; } - float LeftX(bool use_curve = true) { + /** + * @brief Gets the value of the left joystick's x axis, optionally applying a curve. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * @return float The value of the left joystick's x-axis, between -1.0 and 1.0. + */ + float axisLeftX(bool use_curve = true) { if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).first; else return m_LeftX; } - float LeftY(bool use_curve = true) { + /** + * @brief Gets the value of the left joystick's y axis, optionally applying a curve. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * @return float The value of the left joystick's y-axis, between -1.0 and 1.0. + */ + float axisLeftY(bool use_curve = true) { if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).second; else return m_LeftY; } - float RightX(bool use_curve = true) { + /** + * @brief Gets the value of the right joystick's x axis, optionally applying a curve. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * @return float The value of the right joystick's x-axis, between -1.0 and 1.0. + */ + float axisRightX(bool use_curve = true) { if (use_curve && m_right_transformation) return m_right_transformation->get_value({m_RightX, m_RightY}).first; else return m_RightX; } - float RightY(bool use_curve = true) { + /** + * @brief Gets the value of the right joystick's y axis, optionally applying a curve. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * @return float The value of the right joystick's y-axis, between -1.0 and 1.0. + */ + float axisRightY(bool use_curve = true) { if (use_curve && m_right_transformation) return m_right_transformation->get_value({m_RightX, m_RightY}).second; else return m_RightY; } + /** + * @brief Set the transformation to be used for the left joystick. + * + * @param left_transformation The transformation to be used + */ void set_left_transform(Transformation left_transformation) { m_left_transformation = std::move(left_transformation); } + /** + * @brief Set the transformation to be used for the right joystick. + * + * @param right_transformation The transformation to be used + */ void set_right_transform(Transformation right_transformation) { m_right_transformation = std::move(right_transformation); } diff --git a/include/gamepad/joystick_modifier.hpp b/include/gamepad/joystick_modifier.hpp deleted file mode 100644 index 9eacd53..0000000 --- a/include/gamepad/joystick_modifier.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace gamepad { -class AbstractTransformation { - public: - virtual std::pair get_value(std::pair) = 0; - virtual ~AbstractTransformation() = default; -}; - -class Deadband : public AbstractTransformation { - public: - Deadband(float x_deadband, float y_deadband, float x_spread, float y_spread) - : m_x_deadband(x_deadband), - m_y_deadband(y_deadband), - m_x_spread(x_spread), - m_y_spread(y_spread) {} - - Deadband(float x_deadband, float y_deadband) - : Deadband(x_deadband, y_deadband, 0.0, 0.0) {} - - std::pair get_value(std::pair) override; - private: - float m_x_deadband; - float m_y_deadband; - float m_x_spread; - float m_y_spread; -}; - -class ExpoCurve : public AbstractTransformation { - public: - ExpoCurve(float x_curve, float y_curve) - : m_x_curve(x_curve), - m_y_curve(y_curve) {} - - std::pair get_value(std::pair) override; - private: - float m_x_curve; - float m_y_curve; -}; - -class Fisheye : public AbstractTransformation { - public: - Fisheye(float radius) - : m_radius(radius) {} - - std::pair get_value(std::pair) override; - private: - float m_radius; -}; - -class Transformation final { - friend class TransformationBuilder; - public: - std::pair get_value(std::pair); - private: - std::vector> m_all_transforms; -}; - -class TransformationBuilder final { - public: - template T> TransformationBuilder(T first) { - m_transform.m_all_transforms.push_back(std::make_unique(std::move(first))); - } - - TransformationBuilder() = delete; - - template T> TransformationBuilder& and_then(T next) { - m_transform.m_all_transforms.push_back(std::make_unique(std::move(next))); - return *this; - } - - Transformation build() { return std::move(m_transform); } - - operator Transformation() { return std::move(m_transform); } - private: - Transformation m_transform {}; -}; -} // namespace gamepad diff --git a/include/gamepad/joystick_transformation.hpp b/include/gamepad/joystick_transformation.hpp new file mode 100644 index 0000000..5d62dcb --- /dev/null +++ b/include/gamepad/joystick_transformation.hpp @@ -0,0 +1,188 @@ +#pragma once + +#include +#include +#include +#include + +namespace gamepad { + +/** + * @brief An abstract class for joystick transformations. + * + * A transformation takes a coordinate representing the value of the joystick, and returns a transformed coordinate + * value + * + */ +class AbstractTransformation { + public: + /** + * @brief Get the transformed coordinate given the original. + * + * @param original The original value of the joystick + * @return std::pair The transformed value + */ + virtual std::pair get_value(std::pair original) = 0; + virtual ~AbstractTransformation() = default; +}; + +/** + * @brief A joystick transformation that applies a deadband to the joystick values + * + * A deadband makes the joystick value zero when the value is close to zero. This helps prevent drifting, since + * joysticks often do not read exactly zero when released. + */ +class Deadband : public AbstractTransformation { + public: + /** + * @brief Construct a new Deadband object + * + * @param x_deadband The deadband to apply for the x axis. + * @param y_deadband The deadband to apply for the x axis. + * @param x_spread How much the deadband for the x axis should widen. + * @param y_spread How much the deadband for the y axis should widen. + */ + Deadband(float x_deadband, float y_deadband, float x_spread, float y_spread) + : m_x_deadband(x_deadband), + m_y_deadband(y_deadband), + m_x_spread(x_spread), + m_y_spread(y_spread) {} + + /** + * @brief Construct a new Deadband object + * + * @param x_deadband The deadband to apply for the x axis. + * @param y_deadband The deadband to apply for the y axis. + */ + Deadband(float x_deadband, float y_deadband) + : Deadband(x_deadband, y_deadband, 0.0, 0.0) {} + + /** + * @brief Get the joystick coordinate after applying the deadband + * + * @param original The value of the joystick before applying the deadband + * @return std::pair The joystick coordinate, with a deadband applied + */ + std::pair get_value(std::pair original) override; + private: + float m_x_deadband; + float m_y_deadband; + float m_x_spread; + float m_y_spread; +}; + +/** + * @brief A joystick transformation that applies an expo curve to the joystick values + * + * An expo curve allows greater control of the joystick, by reducing the joystick values at low speeds, while still + * allowing you to attain the maximum value of the joysticks. + */ +class ExpoCurve : public AbstractTransformation { + public: + /** + * @brief Construct a new Expo Curve object + * + * @param x_curve How much the x axis should be curved. A higher value curves the joystick value more. + * @param y_curve How much the y axis should be curved. A higher value curves the joystick value more. + */ + ExpoCurve(float x_curve, float y_curve) + : m_x_curve(x_curve), + m_y_curve(y_curve) {} + + /** + * @brief Get the joystick coordinate after applying the curve + * + * @param original The value of the joystick before applying the curve + * @return std::pair The joystick coordinate, with a curve applied + */ + std::pair get_value(std::pair original) override; + private: + float m_x_curve; + float m_y_curve; +}; + +/** + * @brief A joystick transformation that applies a fisheye to the joystick values + * + * The vex controller joysticks don't reach their maximum value in the corners. This can be an issue, especially when + * using single stick arcade. The fisheye "stretches" the joystick values so that they attain their maximum value even + * in the corners of the joysticks. + */ +class Fisheye : public AbstractTransformation { + public: + /** + * @brief Construct a new Fisheye object + * + * @param radius The radius of the rounded circle that forms the corners of the joystick's housing. + */ + Fisheye(float radius) + : m_radius(radius) {} + + /** + * @brief Get the joystick coordinate after applying the fisheye + * + * @param original The value of the joystick before applying the fisheye + * @return std::pair The joystick coordinate, with a fisheye applied + */ + std::pair get_value(std::pair original) override; + private: + float m_radius; +}; + +/** + * @brief A chain of transformations. This class should not be directly used, but should be constructed using the + * TransformationBuilder class. + */ +class Transformation final { + friend class TransformationBuilder; + public: + std::pair get_value(std::pair); + private: + std::vector> m_all_transforms; +}; + +/** + * @brief A class to create a chain of transformations. + * + */ +class TransformationBuilder final { + public: + /** + * @brief Construct a new Transformation Builder object + * + * @param first The transformation that should be used first + */ + template T> TransformationBuilder(T first) { + m_transform.m_all_transforms.push_back(std::make_unique(std::move(first))); + } + + TransformationBuilder() = delete; + + /** + * @brief Add a transformation to the list of transformations to be applied. + * + * @param next The next transformation to be applied after the previous specified transformation + * @return TransformationBuilder& The original Transformation Builder. + */ + template T> TransformationBuilder& and_then(T next) { + m_transform.m_all_transforms.push_back(std::make_unique(std::move(next))); + return *this; + } + + /** + * @brief Generate the final chained transformation + * + * @return Transformation The final chained transformation. This can be passed to set_left_transform/set_right_transform + */ + Transformation build() { return std::move(m_transform); } + + /** + * @brief Generate the final chained transformation + * + * @return Transformation The final chained transformation. This can be passed to set_left_transform/set_right_transform + */ + operator Transformation() { return std::move(m_transform); } + private: + Transformation m_transform {}; +}; +} // namespace gamepad diff --git a/src/gamepad/controller.cpp b/src/gamepad/controller.cpp index 354bc74..76ee695 100644 --- a/src/gamepad/controller.cpp +++ b/src/gamepad/controller.cpp @@ -16,10 +16,10 @@ void Gamepad::update() { this->updateButton(static_cast(i)); } - this->m_LeftX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_X); - this->m_LeftY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y); - this->m_RightX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_X); - this->m_RightY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y); + this->m_LeftX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_X) / 127.0; + this->m_LeftY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y) / 127.0; + this->m_RightX = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_X) / 127.0; + this->m_RightY = this->controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y) / 127.0; } const Button& Gamepad::operator[](pros::controller_digital_e_t button) { return this->*Gamepad::button_to_ptr(button); } diff --git a/src/gamepad/joystick_modifier.cpp b/src/gamepad/joystick_transformation.cpp similarity index 89% rename from src/gamepad/joystick_modifier.cpp rename to src/gamepad/joystick_transformation.cpp index be8258f..10f08d1 100644 --- a/src/gamepad/joystick_modifier.cpp +++ b/src/gamepad/joystick_transformation.cpp @@ -1,4 +1,4 @@ -#include "joystick_modifier.hpp" +#include "joystick_transformation.hpp" #include #include @@ -32,14 +32,14 @@ std::pair Fisheye::get_value(std::pair value) { float y = value.second; float x_abs = abs(x); float y_abs = abs(y); - float j = std::sqrt(m_radius * m_radius - 127 * 127); + float j = std::sqrt(m_radius * m_radius - 1.0 * 1.0); if (x_abs >= j && y_abs >= j) { float theta = std::atan2(y_abs, x_abs); x_abs *= std::acos(abs(std::remainder(theta, 90))); y_abs *= std::acos(abs(std::remainder(theta, 90))); } - x = std::copysign(std::min(127.0f, x_abs), x); - y = std::copysign(std::min(127.0f, y_abs), y); + x = std::copysign(std::min(1.0f, x_abs), x); + y = std::copysign(std::min(1.0f, y_abs), y); return {x, y}; } diff --git a/src/main.cpp b/src/main.cpp index dd04b8d..51a2b1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,5 @@ #include "main.h" #include "gamepad/api.hpp" -#include "gamepad/controller.hpp" -#include "gamepad/joystick_modifier.hpp" -#include "pros/rtos.hpp" #include #include @@ -37,22 +34,22 @@ void aRepeatPress1() { void initialize() { // We can register functions to run when buttons are pressed - gamepad::master.Down().onPress("downPress1", downPress1); + gamepad::master.buttonDown().onPress("downPress1", downPress1); // ...or when they're released - gamepad::master.Up().onRelease("downRelease1", upRelease1); + gamepad::master.buttonUp().onRelease("downRelease1", upRelease1); // There's also the longPress event - gamepad::master.Left().onLongPress("leftLongPress1", leftLongPress1); + gamepad::master.buttonLeft().onLongPress("leftLongPress1", leftLongPress1); // We can have two or even more functions on one button, // just remember to give them different names - gamepad::master.Left().onShortRelease("leftShortRelease", leftShortRelease1); - gamepad::master.Left().onLongRelease("leftLongRelease", leftLongRelease1); + gamepad::master.buttonLeft().onShortRelease("leftShortRelease", leftShortRelease1); + gamepad::master.buttonLeft().onLongRelease("leftLongRelease", leftLongRelease1); // We also have the repeat press event, where we can adjust the timing - gamepad::master.A().set_long_press_threshold(1000); // in ms - gamepad::master.A().set_repeat_cooldown(100); // in ms - gamepad::master.A().onPress("aStartPress", aPress1); - gamepad::master.A().onRepeatPress("aRepeatPress", aRepeatPress1); + gamepad::master.buttonA().set_long_press_threshold(1000); // in ms + gamepad::master.buttonA().set_repeat_cooldown(100); // in ms + gamepad::master.buttonA().onPress("aStartPress", aPress1); + gamepad::master.buttonA().onRepeatPress("aRepeatPress", aRepeatPress1); // And we can use lambda's too - gamepad::master.X().onShortRelease("xShortRelease1", []() { printf("X Short Release!\n"); }); + gamepad::master.buttonX().onShortRelease("xShortRelease1", []() { printf("X Short Release!\n"); }); // set up controller curves: gamepad::master.set_left_transform( @@ -111,8 +108,8 @@ void opcontrol() { // Remember to ALWAYS call update at the start of your while loop! gamepad::master.update(); // We'll use the arcade control scheme - int dir = gamepad::master.LeftY(); // Gets amount forward/backward from left joystick - int turn = gamepad::master.RightX(); // Gets the turn left/right from right joystick + int dir = gamepad::master.axisLeftY(); // Gets amount forward/backward from left joystick + int turn = gamepad::master.axisRightX(); // Gets the turn left/right from right joystick left_mg.move(dir - turn); // Sets left motor voltage right_mg.move(dir + turn); // Sets right motor voltage pros::delay(25); // Wait for 25 ms, then update the motor values again From 9016a4109f3cb79e68b09b4e970b1a4af51fc427 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Mon, 18 Nov 2024 09:54:58 -0800 Subject: [PATCH 13/19] style: :art: fix docs formatting --- include/gamepad/controller.hpp | 56 ++++++++++----------- include/gamepad/joystick_transformation.hpp | 24 ++++----- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/include/gamepad/controller.hpp b/include/gamepad/controller.hpp index 7fb642a..d586285 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/controller.hpp @@ -94,33 +94,33 @@ class Gamepad { const Button& buttonA() { return m_A; } /** - * @brief Gets the value of the left joystick's x axis, optionally applying a curve. - * - * @param use_curve (optional) Whether or not to use the curve; defaults to true. - * @return float The value of the left joystick's x-axis, between -1.0 and 1.0. - */ + * @brief Gets the value of the left joystick's x axis, optionally applying a curve. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * @return float The value of the left joystick's x-axis, between -1.0 and 1.0. + */ float axisLeftX(bool use_curve = true) { if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).first; else return m_LeftX; } /** - * @brief Gets the value of the left joystick's y axis, optionally applying a curve. - * - * @param use_curve (optional) Whether or not to use the curve; defaults to true. - * @return float The value of the left joystick's y-axis, between -1.0 and 1.0. - */ + * @brief Gets the value of the left joystick's y axis, optionally applying a curve. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * @return float The value of the left joystick's y-axis, between -1.0 and 1.0. + */ float axisLeftY(bool use_curve = true) { if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).second; else return m_LeftY; } /** - * @brief Gets the value of the right joystick's x axis, optionally applying a curve. - * - * @param use_curve (optional) Whether or not to use the curve; defaults to true. - * @return float The value of the right joystick's x-axis, between -1.0 and 1.0. - */ + * @brief Gets the value of the right joystick's x axis, optionally applying a curve. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * @return float The value of the right joystick's x-axis, between -1.0 and 1.0. + */ float axisRightX(bool use_curve = true) { if (use_curve && m_right_transformation) return m_right_transformation->get_value({m_RightX, m_RightY}).first; @@ -128,11 +128,11 @@ class Gamepad { } /** - * @brief Gets the value of the right joystick's y axis, optionally applying a curve. - * - * @param use_curve (optional) Whether or not to use the curve; defaults to true. - * @return float The value of the right joystick's y-axis, between -1.0 and 1.0. - */ + * @brief Gets the value of the right joystick's y axis, optionally applying a curve. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * @return float The value of the right joystick's y-axis, between -1.0 and 1.0. + */ float axisRightY(bool use_curve = true) { if (use_curve && m_right_transformation) return m_right_transformation->get_value({m_RightX, m_RightY}).second; @@ -140,19 +140,19 @@ class Gamepad { } /** - * @brief Set the transformation to be used for the left joystick. - * - * @param left_transformation The transformation to be used - */ + * @brief Set the transformation to be used for the left joystick. + * + * @param left_transformation The transformation to be used + */ void set_left_transform(Transformation left_transformation) { m_left_transformation = std::move(left_transformation); } /** - * @brief Set the transformation to be used for the right joystick. - * - * @param right_transformation The transformation to be used - */ + * @brief Set the transformation to be used for the right joystick. + * + * @param right_transformation The transformation to be used + */ void set_right_transform(Transformation right_transformation) { m_right_transformation = std::move(right_transformation); } diff --git a/include/gamepad/joystick_transformation.hpp b/include/gamepad/joystick_transformation.hpp index 5d62dcb..f46f400 100644 --- a/include/gamepad/joystick_transformation.hpp +++ b/include/gamepad/joystick_transformation.hpp @@ -111,10 +111,10 @@ class ExpoCurve : public AbstractTransformation { class Fisheye : public AbstractTransformation { public: /** - * @brief Construct a new Fisheye object - * - * @param radius The radius of the rounded circle that forms the corners of the joystick's housing. - */ + * @brief Construct a new Fisheye object + * + * @param radius The radius of the rounded circle that forms the corners of the joystick's housing. + */ Fisheye(float radius) : m_radius(radius) {} @@ -148,10 +148,10 @@ class Transformation final { class TransformationBuilder final { public: /** - * @brief Construct a new Transformation Builder object - * - * @param first The transformation that should be used first - */ + * @brief Construct a new Transformation Builder object + * + * @param first The transformation that should be used first + */ template T> TransformationBuilder(T first) { m_transform.m_all_transforms.push_back(std::make_unique(std::move(first))); } @@ -170,10 +170,10 @@ class TransformationBuilder final { } /** - * @brief Generate the final chained transformation - * - * @return Transformation The final chained transformation. This can be passed to set_left_transform/set_right_transform - */ + * @brief Generate the final chained transformation + * + * @return Transformation The final chained transformation. This can be passed to set_left_transform/set_right_transform + */ Transformation build() { return std::move(m_transform); } /** From e54e6c73371ac1b48ef61c08b36a13e2e82b8e28 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:20:22 -0800 Subject: [PATCH 14/19] style: :art: remove trailing whitespace --- include/gamepad/controller.hpp | 20 +++++------ include/gamepad/joystick_transformation.hpp | 40 ++++++++++----------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/include/gamepad/controller.hpp b/include/gamepad/controller.hpp index d586285..db1cdf4 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/controller.hpp @@ -95,8 +95,8 @@ class Gamepad { /** * @brief Gets the value of the left joystick's x axis, optionally applying a curve. - * - * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. * @return float The value of the left joystick's x-axis, between -1.0 and 1.0. */ float axisLeftX(bool use_curve = true) { @@ -106,8 +106,8 @@ class Gamepad { /** * @brief Gets the value of the left joystick's y axis, optionally applying a curve. - * - * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. * @return float The value of the left joystick's y-axis, between -1.0 and 1.0. */ float axisLeftY(bool use_curve = true) { @@ -117,8 +117,8 @@ class Gamepad { /** * @brief Gets the value of the right joystick's x axis, optionally applying a curve. - * - * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. * @return float The value of the right joystick's x-axis, between -1.0 and 1.0. */ float axisRightX(bool use_curve = true) { @@ -129,8 +129,8 @@ class Gamepad { /** * @brief Gets the value of the right joystick's y axis, optionally applying a curve. - * - * @param use_curve (optional) Whether or not to use the curve; defaults to true. + * + * @param use_curve (optional) Whether or not to use the curve; defaults to true. * @return float The value of the right joystick's y-axis, between -1.0 and 1.0. */ float axisRightY(bool use_curve = true) { @@ -141,7 +141,7 @@ class Gamepad { /** * @brief Set the transformation to be used for the left joystick. - * + * * @param left_transformation The transformation to be used */ void set_left_transform(Transformation left_transformation) { @@ -150,7 +150,7 @@ class Gamepad { /** * @brief Set the transformation to be used for the right joystick. - * + * * @param right_transformation The transformation to be used */ void set_right_transform(Transformation right_transformation) { diff --git a/include/gamepad/joystick_transformation.hpp b/include/gamepad/joystick_transformation.hpp index f46f400..eee416b 100644 --- a/include/gamepad/joystick_transformation.hpp +++ b/include/gamepad/joystick_transformation.hpp @@ -8,17 +8,17 @@ namespace gamepad { /** - * @brief An abstract class for joystick transformations. - * - * A transformation takes a coordinate representing the value of the joystick, and returns a transformed coordinate + * @brief An abstract class for joystick transformations. + * + * A transformation takes a coordinate representing the value of the joystick, and returns a transformed coordinate * value - * + * */ class AbstractTransformation { public: /** * @brief Get the transformed coordinate given the original. - * + * * @param original The original value of the joystick * @return std::pair The transformed value */ @@ -28,7 +28,7 @@ class AbstractTransformation { /** * @brief A joystick transformation that applies a deadband to the joystick values - * + * * A deadband makes the joystick value zero when the value is close to zero. This helps prevent drifting, since * joysticks often do not read exactly zero when released. */ @@ -36,7 +36,7 @@ class Deadband : public AbstractTransformation { public: /** * @brief Construct a new Deadband object - * + * * @param x_deadband The deadband to apply for the x axis. * @param y_deadband The deadband to apply for the x axis. * @param x_spread How much the deadband for the x axis should widen. @@ -50,7 +50,7 @@ class Deadband : public AbstractTransformation { /** * @brief Construct a new Deadband object - * + * * @param x_deadband The deadband to apply for the x axis. * @param y_deadband The deadband to apply for the y axis. */ @@ -59,7 +59,7 @@ class Deadband : public AbstractTransformation { /** * @brief Get the joystick coordinate after applying the deadband - * + * * @param original The value of the joystick before applying the deadband * @return std::pair The joystick coordinate, with a deadband applied */ @@ -73,7 +73,7 @@ class Deadband : public AbstractTransformation { /** * @brief A joystick transformation that applies an expo curve to the joystick values - * + * * An expo curve allows greater control of the joystick, by reducing the joystick values at low speeds, while still * allowing you to attain the maximum value of the joysticks. */ @@ -81,7 +81,7 @@ class ExpoCurve : public AbstractTransformation { public: /** * @brief Construct a new Expo Curve object - * + * * @param x_curve How much the x axis should be curved. A higher value curves the joystick value more. * @param y_curve How much the y axis should be curved. A higher value curves the joystick value more. */ @@ -91,7 +91,7 @@ class ExpoCurve : public AbstractTransformation { /** * @brief Get the joystick coordinate after applying the curve - * + * * @param original The value of the joystick before applying the curve * @return std::pair The joystick coordinate, with a curve applied */ @@ -112,15 +112,15 @@ class Fisheye : public AbstractTransformation { public: /** * @brief Construct a new Fisheye object - * + * * @param radius The radius of the rounded circle that forms the corners of the joystick's housing. */ Fisheye(float radius) : m_radius(radius) {} - + /** * @brief Get the joystick coordinate after applying the fisheye - * + * * @param original The value of the joystick before applying the fisheye * @return std::pair The joystick coordinate, with a fisheye applied */ @@ -143,13 +143,13 @@ class Transformation final { /** * @brief A class to create a chain of transformations. - * + * */ class TransformationBuilder final { public: /** * @brief Construct a new Transformation Builder object - * + * * @param first The transformation that should be used first */ template T> TransformationBuilder(T first) { @@ -160,7 +160,7 @@ class TransformationBuilder final { /** * @brief Add a transformation to the list of transformations to be applied. - * + * * @param next The next transformation to be applied after the previous specified transformation * @return TransformationBuilder& The original Transformation Builder. */ @@ -171,14 +171,14 @@ class TransformationBuilder final { /** * @brief Generate the final chained transformation - * + * * @return Transformation The final chained transformation. This can be passed to set_left_transform/set_right_transform */ Transformation build() { return std::move(m_transform); } /** * @brief Generate the final chained transformation - * + * * @return Transformation The final chained transformation. This can be passed to set_left_transform/set_right_transform */ operator Transformation() { return std::move(m_transform); } From a1cb6d7b70ee50afb92fcdce00b6cdc1a2e9be89 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:09:57 +0000 Subject: [PATCH 15/19] style: :art: fix formatting of doc comments --- include/gamepad/joystick_transformation.hpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/include/gamepad/joystick_transformation.hpp b/include/gamepad/joystick_transformation.hpp index eee416b..6f028db 100644 --- a/include/gamepad/joystick_transformation.hpp +++ b/include/gamepad/joystick_transformation.hpp @@ -16,12 +16,12 @@ namespace gamepad { */ class AbstractTransformation { public: - /** - * @brief Get the transformed coordinate given the original. - * - * @param original The original value of the joystick - * @return std::pair The transformed value - */ + /** + * @brief Get the transformed coordinate given the original. + * + * @param original The original value of the joystick + * @return std::pair The transformed value + */ virtual std::pair get_value(std::pair original) = 0; virtual ~AbstractTransformation() = default; }; @@ -172,14 +172,16 @@ class TransformationBuilder final { /** * @brief Generate the final chained transformation * - * @return Transformation The final chained transformation. This can be passed to set_left_transform/set_right_transform + * @return Transformation The final chained transformation. This can be passed to + * set_left_transform/set_right_transform */ Transformation build() { return std::move(m_transform); } /** * @brief Generate the final chained transformation * - * @return Transformation The final chained transformation. This can be passed to set_left_transform/set_right_transform + * @return Transformation The final chained transformation. This can be passed to + * set_left_transform/set_right_transform */ operator Transformation() { return std::move(m_transform); } private: From 453a2df202cd11298ad994a4e27492b062ced355 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:15:16 +0000 Subject: [PATCH 16/19] refactor: :recycle: Move methods from header to src file --- include/gamepad/controller.hpp | 54 ++++++++++++---------------------- src/gamepad/controller.cpp | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/include/gamepad/controller.hpp b/include/gamepad/controller.hpp index db1cdf4..1073fa4 100644 --- a/include/gamepad/controller.hpp +++ b/include/gamepad/controller.hpp @@ -58,40 +58,40 @@ class Gamepad { float operator[](pros::controller_analog_e_t joystick); /// The L1 button on the top of the controller. - const Button& buttonL1() { return m_L1; } + const Button& buttonL1(); /// The L2 button on the top of the controller. - const Button& buttonL2() { return m_L2; } + const Button& buttonL2(); /// The R1 button on the top of the controller. - const Button& buttonR1() { return m_R1; } + const Button& buttonR1(); /// The R2 button on the top of the controller. - const Button& buttonR2() { return m_R2; } + const Button& buttonR2(); /// The up arrow button on the front of the controller. - const Button& buttonUp() { return m_Up; } + const Button& buttonUp(); /// The down arrow button on the front of the controller. - const Button& buttonDown() { return m_Down; } + const Button& buttonDown(); /// The left arrow button on the front of the controller. - const Button& buttonLeft() { return m_Left; } + const Button& buttonLeft(); /// The right arrow button on the front of the controller. - const Button& buttonRight() { return m_Right; } + const Button& buttonRight(); /// The X arrow button on the front of the controller. - const Button& buttonX() { return m_X; } + const Button& buttonX(); /// The B arrow button on the front of the controller. - const Button& buttonB() { return m_B; } + const Button& buttonB(); /// The Y arrow button on the front of the controller. - const Button& buttonY() { return m_Y; } + const Button& buttonY(); /// The A arrow button on the front of the controller. - const Button& buttonA() { return m_A; } + const Button& buttonA(); /** * @brief Gets the value of the left joystick's x axis, optionally applying a curve. @@ -99,10 +99,7 @@ class Gamepad { * @param use_curve (optional) Whether or not to use the curve; defaults to true. * @return float The value of the left joystick's x-axis, between -1.0 and 1.0. */ - float axisLeftX(bool use_curve = true) { - if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).first; - else return m_LeftX; - } + float axisLeftX(bool use_curve = true); /** * @brief Gets the value of the left joystick's y axis, optionally applying a curve. @@ -110,10 +107,7 @@ class Gamepad { * @param use_curve (optional) Whether or not to use the curve; defaults to true. * @return float The value of the left joystick's y-axis, between -1.0 and 1.0. */ - float axisLeftY(bool use_curve = true) { - if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).second; - else return m_LeftY; - } + float axisLeftY(bool use_curve = true); /** * @brief Gets the value of the right joystick's x axis, optionally applying a curve. @@ -121,11 +115,7 @@ class Gamepad { * @param use_curve (optional) Whether or not to use the curve; defaults to true. * @return float The value of the right joystick's x-axis, between -1.0 and 1.0. */ - float axisRightX(bool use_curve = true) { - if (use_curve && m_right_transformation) - return m_right_transformation->get_value({m_RightX, m_RightY}).first; - else return m_RightX; - } + float axisRightX(bool use_curve = true); /** * @brief Gets the value of the right joystick's y axis, optionally applying a curve. @@ -133,29 +123,21 @@ class Gamepad { * @param use_curve (optional) Whether or not to use the curve; defaults to true. * @return float The value of the right joystick's y-axis, between -1.0 and 1.0. */ - float axisRightY(bool use_curve = true) { - if (use_curve && m_right_transformation) - return m_right_transformation->get_value({m_RightX, m_RightY}).second; - else return m_RightY; - } + float axisRightY(bool use_curve = true); /** * @brief Set the transformation to be used for the left joystick. * * @param left_transformation The transformation to be used */ - void set_left_transform(Transformation left_transformation) { - m_left_transformation = std::move(left_transformation); - } + void set_left_transform(Transformation left_transformation); /** * @brief Set the transformation to be used for the right joystick. * * @param right_transformation The transformation to be used */ - void set_right_transform(Transformation right_transformation) { - m_right_transformation = std::move(right_transformation); - } + void set_right_transform(Transformation right_transformation); /// The master controller, same as @ref gamepad::master static Gamepad master; diff --git a/src/gamepad/controller.cpp b/src/gamepad/controller.cpp index 76ee695..8396988 100644 --- a/src/gamepad/controller.cpp +++ b/src/gamepad/controller.cpp @@ -42,6 +42,58 @@ std::string Gamepad::unique_name() { return std::to_string(i++) + "_internal"; } +const Button& Gamepad::buttonL1() { return m_L1; } + +const Button& Gamepad::buttonL2() { return m_L2; } + +const Button& Gamepad::buttonR1() { return m_R1; } + +const Button& Gamepad::buttonR2() { return m_R2; } + +const Button& Gamepad::buttonUp() { return m_Up; } + +const Button& Gamepad::buttonDown() { return m_Down; } + +const Button& Gamepad::buttonLeft() { return m_Left; } + +const Button& Gamepad::buttonRight() { return m_Right; } + +const Button& Gamepad::buttonX() { return m_X; } + +const Button& Gamepad::buttonB() { return m_B; } + +const Button& Gamepad::buttonY() { return m_Y; } + +const Button& Gamepad::buttonA() { return m_A; } + +float Gamepad::axisLeftX(bool use_curve) { + if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).first; + else return m_LeftX; +} + +float Gamepad::axisLeftY(bool use_curve) { + if (use_curve && m_left_transformation) return m_left_transformation->get_value({m_LeftX, m_LeftY}).second; + else return m_LeftY; +} + +float Gamepad::axisRightX(bool use_curve) { + if (use_curve && m_right_transformation) return m_right_transformation->get_value({m_RightX, m_RightY}).first; + else return m_RightX; +} + +float Gamepad::axisRightY(bool use_curve) { + if (use_curve && m_right_transformation) return m_right_transformation->get_value({m_RightX, m_RightY}).second; + else return m_RightY; +} + +void Gamepad::set_left_transform(Transformation left_transformation) { + m_left_transformation = std::move(left_transformation); +} + +void Gamepad::set_right_transform(Transformation right_transformation) { + m_right_transformation = std::move(right_transformation); +} + Button Gamepad::*Gamepad::button_to_ptr(pros::controller_digital_e_t button) { switch (button) { case pros::E_CONTROLLER_DIGITAL_L1: return &Gamepad::m_L1; From 335a1f4732f210cf7c8f1ac2d1a4a37723437740 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:32:39 +0000 Subject: [PATCH 17/19] fix: :bug: Fix outdated code in main.cpp --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 51a2b1a..0b00a45 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,7 +53,7 @@ void initialize() { // set up controller curves: gamepad::master.set_left_transform( - gamepad::TransformationBuilder(gamepad::Deadband(5, 5)).and_then(gamepad::ExpoCurve(2, 2))); + gamepad::TransformationBuilder(gamepad::Deadband(0.05, 0.05)).and_then(gamepad::ExpoCurve(2, 2))); } /** @@ -108,8 +108,8 @@ void opcontrol() { // Remember to ALWAYS call update at the start of your while loop! gamepad::master.update(); // We'll use the arcade control scheme - int dir = gamepad::master.axisLeftY(); // Gets amount forward/backward from left joystick - int turn = gamepad::master.axisRightX(); // Gets the turn left/right from right joystick + int dir = gamepad::master.axisLeftY() * 127; // Gets amount forward/backward from left joystick + int turn = gamepad::master.axisRightX() * 127; // Gets the turn left/right from right joystick left_mg.move(dir - turn); // Sets left motor voltage right_mg.move(dir + turn); // Sets right motor voltage pros::delay(25); // Wait for 25 ms, then update the motor values again From 1c07cd36ff742283cbf420ba17e5f4f575690a23 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:28:02 -0800 Subject: [PATCH 18/19] fix: :bug: Fix bugs in Deadband, ExpoCurve, and Transformation (Note: Fisheye is still broken) --- src/gamepad/joystick_transformation.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/gamepad/joystick_transformation.cpp b/src/gamepad/joystick_transformation.cpp index 10f08d1..18ec32a 100644 --- a/src/gamepad/joystick_transformation.cpp +++ b/src/gamepad/joystick_transformation.cpp @@ -12,8 +12,8 @@ std::pair Deadband::get_value(std::pair value) { float y = value.second; float x_deadband = m_x_deadband + abs(y) * m_x_spread; float y_deadband = m_y_deadband + abs(x) * m_y_spread; - float x_scale = 1 - x_deadband; - float y_scale = 1 - y_deadband; + float x_scale = 1.0 / (1.0 - x_deadband); + float y_scale = 1.0 / (1.0 - y_deadband); x = copysign(abs(x) < x_deadband ? 0 : (abs(x) - x_deadband) * x_scale, x); y = copysign(abs(y) < y_deadband ? 0 : (abs(y) - y_deadband) * y_scale, y); return {x, y}; @@ -23,7 +23,7 @@ std::pair ExpoCurve::get_value(std::pair value) { float x = value.first; float y = value.second; x = copysign(pow(abs(x), m_x_curve), x); - x = copysign(pow(abs(y), m_y_curve), y); + y = copysign(pow(abs(y), m_y_curve), y); return {x, y}; } @@ -44,8 +44,7 @@ std::pair Fisheye::get_value(std::pair value) { } std::pair Transformation::get_value(std::pair value) { - std::pair ret_value {}; - return std::accumulate(m_all_transforms.begin(), m_all_transforms.end(), ret_value, + return std::accumulate(m_all_transforms.begin(), m_all_transforms.end(), value, [](auto last_val, auto& next_transform) { return next_transform->get_value(last_val); }); } } // namespace gamepad \ No newline at end of file From 371182de78831aeab0174733d3687edeb78f5aa6 Mon Sep 17 00:00:00 2001 From: ion098 <146852218+ion098@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:32:46 -0800 Subject: [PATCH 19/19] fix: :bug: Fix bug in Fisheye --- src/gamepad/joystick_transformation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gamepad/joystick_transformation.cpp b/src/gamepad/joystick_transformation.cpp index 18ec32a..225c2d1 100644 --- a/src/gamepad/joystick_transformation.cpp +++ b/src/gamepad/joystick_transformation.cpp @@ -35,8 +35,8 @@ std::pair Fisheye::get_value(std::pair value) { float j = std::sqrt(m_radius * m_radius - 1.0 * 1.0); if (x_abs >= j && y_abs >= j) { float theta = std::atan2(y_abs, x_abs); - x_abs *= std::acos(abs(std::remainder(theta, 90))); - y_abs *= std::acos(abs(std::remainder(theta, 90))); + x_abs *= m_radius / std::cos(abs(std::remainder(theta, 90))); + y_abs *= m_radius / std::cos(abs(std::remainder(theta, 90))); } x = std::copysign(std::min(1.0f, x_abs), x); y = std::copysign(std::min(1.0f, y_abs), y);