Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Joystick enhancements #8

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
749edf4
feat: :sparkles: add drive curve implementation
ion098 Jun 17, 2024
1ca81b0
feat: :sparkles: Add joystick curves to controller
ion098 Jun 17, 2024
0772340
style: :art: Format event_handler.hpp
ion098 Jun 17, 2024
846ba1a
fix: :bug: Fix variable name typo
ion098 Jun 25, 2024
734183b
refactor: :construction: Remove deadband from curve
ion098 Jun 25, 2024
59c588b
Merge branch 'main' of https://github.com/LemLib/Gamepad into feature…
ion098 Oct 2, 2024
ae85554
fix: :pencil2: remove merge conflict markers
ion098 Oct 29, 2024
e3e6aa2
Merge branch 'main' into feature/joystick-enhancements
ion098 Oct 29, 2024
982c53b
fix: :bug: Fix compile errors from merging
ion098 Oct 29, 2024
e7f7495
refactor: :construction: Start work on joystick modifier classes
ion098 Oct 30, 2024
297295a
feat: :sparkles: Add fisheye joystick modifier
ion098 Nov 2, 2024
83c8315
feat: :construction: Add ability to add transformations to gamepad
ion098 Nov 4, 2024
67695e4
style: :art: Fix formatting in joystick_modifier.hpp
ion098 Nov 4, 2024
fd99f2a
docs: :memo: Add docs to Gamepad and Transformations
ion098 Nov 16, 2024
9016a41
style: :art: fix docs formatting
ion098 Nov 18, 2024
e54e6c7
style: :art: remove trailing whitespace
ion098 Nov 18, 2024
a1cb6d7
style: :art: fix formatting of doc comments
ion098 Nov 19, 2024
453a2df
refactor: :recycle: Move methods from header to src file
ion098 Nov 19, 2024
335a1f4
fix: :bug: Fix outdated code in main.cpp
ion098 Nov 19, 2024
261d886
Merge branch 'main' into feature/joystick-enhancements
ion098 Nov 21, 2024
1c07cd3
fix: :bug: Fix bugs in Deadband, ExpoCurve, and Transformation
ion098 Nov 28, 2024
371182d
fix: :bug: Fix bug in Fisheye
ion098 Nov 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 90 additions & 18 deletions include/gamepad/controller.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#pragma once

#include "pros/misc.h"
#include <optional>
#include <string>

#include "button.hpp"
#include "joystick_transformation.hpp"
#include "pros/misc.hpp"

namespace gamepad {
Expand Down Expand Up @@ -45,30 +46,99 @@ 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 {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;

/// The L1 button on the top of the controller.
const Button& buttonL1();

/// The L2 button on the top of the controller.
const Button& buttonL2();

/// The R1 button on the top of the controller.
const Button& buttonR1();

/// The R2 button on the top of the controller.
const Button& buttonR2();

/// The up arrow button on the front of the controller.
const Button& buttonUp();

/// The down arrow button on the front of the controller.
const Button& buttonDown();

/// The left arrow button on the front of the controller.
const Button& buttonLeft();

/// The right arrow button on the front of the controller.
const Button& buttonRight();

/// The X arrow button on the front of the controller.
const Button& buttonX();

/// The B arrow button on the front of the controller.
const Button& buttonB();

/// The Y arrow button on the front of the controller.
const Button& buttonY();

/// The A arrow button on the front of the controller.
const Button& buttonA();

/**
* @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);

/**
* @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);

/**
* @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);

/**
* @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);

/**
* @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);

/**
* @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);

/// The master controller, same as @ref gamepad::master
static Gamepad master;
/// The partner controller, same as @ref gamepad::partner
Expand All @@ -81,6 +151,8 @@ class Gamepad {
m_A {};
float m_LeftX = 0, m_LeftY = 0, m_RightX = 0, m_RightY = 0;
Button Fake {};
std::optional<Transformation> m_left_transformation {std::nullopt};
std::optional<Transformation> m_right_transformation {std::nullopt};
/**
* @brief Gets a unique name for a listener that will not conflict with user listener names.
*
Expand Down
190 changes: 190 additions & 0 deletions include/gamepad/joystick_transformation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#pragma once

#include <concepts>
#include <memory>
#include <utility>
#include <vector>

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<float, float> The transformed value
*/
virtual std::pair<float, float> get_value(std::pair<float, float> 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<float, float> The joystick coordinate, with a deadband applied
*/
std::pair<float, float> get_value(std::pair<float, float> 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<float, float> The joystick coordinate, with a curve applied
*/
std::pair<float, float> get_value(std::pair<float, float> 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<float, float> The joystick coordinate, with a fisheye applied
*/
std::pair<float, float> get_value(std::pair<float, float> 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<float, float> get_value(std::pair<float, float>);
private:
std::vector<std::unique_ptr<AbstractTransformation>> 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 <std::derived_from<AbstractTransformation> T> TransformationBuilder(T first) {
m_transform.m_all_transforms.push_back(std::make_unique<T>(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 <std::derived_from<AbstractTransformation> T> TransformationBuilder& and_then(T next) {
m_transform.m_all_transforms.push_back(std::make_unique<T>(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
Loading