diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 4e55a063a2..9a72ae63cd 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -56,7 +56,7 @@ the consequences for any action they deem in violation of this Code of Conduct: **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. -**Consequence**: A private, written warning from community leaders, providing +**Consequence**: A warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. diff --git a/docs/build.gradle b/docs/build.gradle index 489424de2b..7539f4665b 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -53,12 +53,16 @@ doxygen { // See below maven and https://doxygen.nl/download.html for provided binaries // Ensure theme.css (from https://github.com/jothepro/doxygen-awesome-css) is compatible with // doxygen version when updating + executables { + doxygen { + // Note: has no effect if not on an x86_64 platform - you need to have a global install available on your + // PATH for the doxygen plugin to run + executableByVersion('1.12.0') - String arch = System.getProperty("os.arch"); - if (arch.equals("x86_64") || arch.equals("amd64")) { - executables { - doxygen { - executableByVersion('1.12.0') + String arch = System.getProperty("os.arch"); + if (!(arch.equals("x86_64") || arch.equals("amd64"))) { + // Search for a local doxygen install + executableBySearchPath('doxygen') } } } diff --git a/upstream_utils/eigen.py b/upstream_utils/eigen.py index 330eaae579..44d2bc6934 100755 --- a/upstream_utils/eigen.py +++ b/upstream_utils/eigen.py @@ -154,8 +154,7 @@ def copy_upstream_src(wpilib_root: Path): def main(): name = "eigen" url = "https://gitlab.com/libeigen/eigen.git" - # 5.0.0 release as of 2025-09-23 - tag = "d65cda87c1a673047b59b20a9f9e165a452f91e9" + tag = "5.0.0" eigen = Lib(name, url, tag, copy_upstream_src) eigen.main() diff --git a/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp b/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp index 986e1afc97..e576a74171 100644 --- a/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp +++ b/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp @@ -112,8 +112,8 @@ DifferentialDrive::WheelSpeeds DifferentialDrive::ArcadeDriveIK( // Square the inputs (while preserving the sign) to increase fine control // while permitting full power. if (squareInputs) { - xSpeed = CopySignPow(xSpeed, 2); - zRotation = CopySignPow(zRotation, 2); + xSpeed = CopyDirectionPow(xSpeed, 2); + zRotation = CopyDirectionPow(zRotation, 2); } double leftSpeed = xSpeed - zRotation; @@ -167,8 +167,8 @@ DifferentialDrive::WheelSpeeds DifferentialDrive::TankDriveIK( // Square the inputs (while preserving the sign) to increase fine control // while permitting full power. if (squareInputs) { - leftSpeed = CopySignPow(leftSpeed, 2); - rightSpeed = CopySignPow(rightSpeed, 2); + leftSpeed = CopyDirectionPow(leftSpeed, 2); + rightSpeed = CopyDirectionPow(rightSpeed, 2); } return {leftSpeed, rightSpeed}; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java index 99fe0f9939..9a63b395c9 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java @@ -260,8 +260,8 @@ public class DifferentialDrive extends RobotDriveBase implements Sendable, AutoC // Square the inputs (while preserving the sign) to increase fine control // while permitting full power. if (squareInputs) { - xSpeed = MathUtil.copySignPow(xSpeed, 2); - zRotation = MathUtil.copySignPow(zRotation, 2); + xSpeed = MathUtil.copyDirectionPow(xSpeed, 2); + zRotation = MathUtil.copyDirectionPow(zRotation, 2); } double leftSpeed = xSpeed - zRotation; @@ -335,8 +335,8 @@ public class DifferentialDrive extends RobotDriveBase implements Sendable, AutoC // Square the inputs (while preserving the sign) to increase fine control // while permitting full power. if (squareInputs) { - leftSpeed = MathUtil.copySignPow(leftSpeed, 2); - rightSpeed = MathUtil.copySignPow(rightSpeed, 2); + leftSpeed = MathUtil.copyDirectionPow(leftSpeed, 2); + rightSpeed = MathUtil.copyDirectionPow(rightSpeed, 2); } return new WheelSpeeds(leftSpeed, rightSpeed); diff --git a/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java b/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java index 4004ab4d2a..43093d14b7 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java @@ -116,6 +116,39 @@ public final class MathUtil { return applyDeadband(value, deadband, 1); } + /** + * Returns a zero vector if the given vector is within the specified distance from the origin. The + * remaining distance between the deadband and the maximum distance is scaled from the origin to + * the maximum distance. + * + * @param value Value to clip. + * @param deadband Distance from origin. + * @param maxMagnitude The maximum distance from the origin of the input. Can be infinite. + * @param The number of rows in the vector. + * @return The value after the deadband is applied. + */ + public static Vector applyDeadband( + Vector value, double deadband, double maxMagnitude) { + if (value.norm() < 1e-9) { + return value.times(0); + } + return value.unit().times(applyDeadband(value.norm(), deadband, maxMagnitude)); + } + + /** + * Returns a zero vector if the given vector is within the specified distance from the origin. The + * remaining distance between the deadband and a distance of 1.0 is scaled from the origin to a + * distance of 1.0. + * + * @param value Value to clip. + * @param deadband Distance from origin. + * @param The number of rows in the vector. + * @return The value after the deadband is applied. + */ + public static Vector applyDeadband(Vector value, double deadband) { + return applyDeadband(value, deadband, 1); + } + /** * Raises the input to the power of the given exponent while preserving its sign. * @@ -133,7 +166,7 @@ public final class MathUtil { * @param maxMagnitude The maximum expected absolute value of input. Must be positive. * @return The transformed value with the same sign and scaled to the input range. */ - public static double copySignPow(double value, double exponent, double maxMagnitude) { + public static double copyDirectionPow(double value, double exponent, double maxMagnitude) { return Math.copySign(Math.pow(Math.abs(value) / maxMagnitude, exponent), value) * maxMagnitude; } @@ -148,8 +181,44 @@ public final class MathUtil { * positive. * @return The transformed value with the same sign. */ - public static double copySignPow(double value, double exponent) { - return copySignPow(value, exponent, 1); + public static double copyDirectionPow(double value, double exponent) { + return copyDirectionPow(value, exponent, 1); + } + + /** + * Raises the norm of the input to the power of the given exponent while preserving its direction. + * + *

The function normalizes the norm of the input to the range [0, 1] based on the maximum + * distance, raises it to the power of the exponent, then scales the result back to the original + * range. This keeps the value in the original max distance and gives consistent curve behavior + * regardless of the input norm's scale. + * + * @param value The input vector to transform. + * @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared curve). Must be + * positive. + * @param maxMagnitude The maximum expected distance from origin of input. Must be positive. + * @param The number of rows in the vector. + * @return The transformed value with the same direction and norm scaled to the input range. + */ + public static Vector copyDirectionPow( + Vector value, double exponent, double maxMagnitude) { + if (value.norm() < 1e-9) { + return value.times(0); + } + return value.unit().times(copyDirectionPow(value.norm(), exponent, maxMagnitude)); + } + + /** + * Raises the norm of the input to the power of the given exponent while preserving its direction. + * + * @param value The input vector to transform. + * @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared curve). Must be + * positive. + * @param The number of rows in the vector. + * @return The transformed value with the same direction. + */ + public static Vector copyDirectionPow(Vector value, double exponent) { + return copyDirectionPow(value, exponent, 1); } /** diff --git a/wpimath/src/main/native/include/frc/MathUtil.h b/wpimath/src/main/native/include/frc/MathUtil.h index 97705811f9..66f4008781 100644 --- a/wpimath/src/main/native/include/frc/MathUtil.h +++ b/wpimath/src/main/native/include/frc/MathUtil.h @@ -94,15 +94,41 @@ constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) { } } +/** + * Returns a zero vector if the given vector is within the specified + * distance from the origin. The remaining distance between the deadband and the + * maximum distance is scaled from the origin to the maximum distance. + * + * @param value Value to clip. + * @param deadband Distance from origin. + * @param maxMagnitude The maximum distance from the origin of the input + * (defaults to 1). Can be infinite. + * @return The value after the deadband is applied. + */ +template + requires std::is_arithmetic_v || units::traits::is_unit_t_v +Eigen::Vector ApplyDeadband(const Eigen::Vector& value, T deadband, + T maxMagnitude = T{1.0}) { + if constexpr (std::is_arithmetic_v) { + if (value.norm() < T{1e-9}) { + return Eigen::Vector::Zero(); + } + return value.normalized() * + ApplyDeadband(value.norm(), deadband, maxMagnitude); + } else { + const Eigen::Vector asDouble = value.template cast(); + const Eigen::Vector processed = + ApplyDeadband(asDouble, deadband.value(), maxMagnitude.value()); + return processed.template cast(); + } +} + /** * Raises the input to the power of the given exponent while preserving its * sign. * * The function normalizes the input value to the range [0, 1] based on the - * maximum magnitude, raises it to the power of the exponent, then scales the - * result back to the original range and copying the sign. This keeps the value - * in the original range and gives consistent curve behavior regardless of the - * input value's scale. + * maximum magnitude so that the output stays in the range. * * This is useful for applying smoother or more aggressive control response * curves (e.g. joystick input shaping). @@ -110,14 +136,15 @@ constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) { * @param value The input value to transform. * @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared * curve). Must be positive. - * @param maxMagnitude The maximum expected absolute value of input. Must be - * positive. + * @param maxMagnitude The maximum expected absolute value of input (defaults to + * 1). Must be positive. * @return The transformed value with the same sign and scaled to the input * range. */ template requires std::is_arithmetic_v || units::traits::is_unit_t_v -constexpr T CopySignPow(T value, double exponent, T maxMagnitude = T{1.0}) { +constexpr T CopyDirectionPow(T value, double exponent, + T maxMagnitude = T{1.0}) { if constexpr (std::is_arithmetic_v) { return gcem::copysign( gcem::pow(gcem::abs(value) / maxMagnitude, exponent) * maxMagnitude, @@ -130,6 +157,42 @@ constexpr T CopySignPow(T value, double exponent, T maxMagnitude = T{1.0}) { } } +/** + * Raises the norm of the input to the power of the given exponent while + * preserving its direction. + * + * The function normalizes the input value to the range [0, 1] based on the + * maximum magnitude so that the output stays in the range. + * + * This is useful for applying smoother or more aggressive control response + * curves (e.g. joystick input shaping). + * + * @param value The input vector to transform. + * @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared + * curve). Must be positive. + * @param maxMagnitude The maximum expected distance from origin of input + * (defaults to 1). Must be positive. + * @return The transformed value with the same direction and norm scaled to + * the input range. + */ +template + requires std::is_arithmetic_v || units::traits::is_unit_t_v +Eigen::Vector CopyDirectionPow(const Eigen::Vector& value, + double exponent, T maxMagnitude = T{1.0}) { + if constexpr (std::is_arithmetic_v) { + if (value.norm() < T{1e-9}) { + return Eigen::Vector::Zero(); + } + return value.normalized() * + CopyDirectionPow(value.norm(), exponent, maxMagnitude); + } else { + const Eigen::Vector asDouble = value.template cast(); + const Eigen::Vector processed = + CopyDirectionPow(asDouble, exponent, maxMagnitude.value()); + return processed.template cast(); + } +} + /** * Returns modulus of input. * @@ -279,6 +342,7 @@ constexpr Translation2d SlewRateLimit(const Translation2d& current, } if (dist > maxVelocity * dt) { // Move maximum allowed amount in direction of the difference + // NOLINTNEXTLINE(bugprone-integer-division) return current + diff * (maxVelocity * dt / dist); } return next; @@ -309,6 +373,7 @@ constexpr Translation3d SlewRateLimit(const Translation3d& current, } if (dist > maxVelocity * dt) { // Move maximum allowed amount in direction of the difference + // NOLINTNEXTLINE(bugprone-integer-division) return current + diff * (maxVelocity * dt / dist); } return next; diff --git a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/Version b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/Version index db0f3bf2aa..91936c2e21 100644 --- a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/Version +++ b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/Version @@ -7,8 +7,8 @@ #define EIGEN_MAJOR_VERSION 5 #define EIGEN_MINOR_VERSION 0 #define EIGEN_PATCH_VERSION 0 -#define EIGEN_PRERELEASE_VERSION -#define EIGEN_BUILD_VERSION +#define EIGEN_PRERELEASE_VERSION "" +#define EIGEN_BUILD_VERSION "" #define EIGEN_VERSION_STRING "5.0.0" #endif // EIGEN_VERSION_H diff --git a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/CoreEvaluators.h b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/CoreEvaluators.h index c9b2d2d282..60857e2cc4 100644 --- a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/CoreEvaluators.h +++ b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/CoreEvaluators.h @@ -1048,22 +1048,33 @@ struct ternary_evaluator, IndexBased Data m_d; }; -// specialization for expressions like (a < b).select(c, d) to enable full vectorization template -struct evaluator, Arg1, Arg2, - CwiseBinaryOp, CmpLhsType, CmpRhsType>>> - : public ternary_evaluator< - CwiseTernaryOp, Arg1, Arg2, - CwiseBinaryOp, CmpLhsType, CmpRhsType>>> { +struct scalar_boolean_select_spec { using DummyTernaryOp = scalar_boolean_select_op; using DummyArg3 = CwiseBinaryOp, CmpLhsType, CmpRhsType>; using DummyXprType = CwiseTernaryOp; - using TernaryOp = scalar_boolean_select_op; - using Arg3 = CwiseBinaryOp, CmpLhsType, CmpRhsType>; + // only use the typed comparison if it is vectorized + static constexpr bool UseTyped = functor_traits>::PacketAccess; + using CondScalar = std::conditional_t; + + using TernaryOp = scalar_boolean_select_op; + using Arg3 = CwiseBinaryOp, CmpLhsType, CmpRhsType>; using XprType = CwiseTernaryOp; using Base = ternary_evaluator; +}; + +// specialization for expressions like (a < b).select(c, d) to enable full vectorization +template +struct evaluator, Arg1, Arg2, + CwiseBinaryOp, CmpLhsType, CmpRhsType>>> + : public scalar_boolean_select_spec::Base { + using Helper = scalar_boolean_select_spec; + using Base = typename Helper::Base; + using DummyXprType = typename Helper::DummyXprType; + using Arg3 = typename Helper::Arg3; + using XprType = typename Helper::XprType; EIGEN_DEVICE_FUNC explicit evaluator(const DummyXprType& xpr) : Base(XprType(xpr.arg1(), xpr.arg2(), Arg3(xpr.arg3().lhs(), xpr.arg3().rhs()))) {} diff --git a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/ProductEvaluators.h b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/ProductEvaluators.h index 5955e496f6..be55be5e89 100644 --- a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/ProductEvaluators.h +++ b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/ProductEvaluators.h @@ -1272,6 +1272,14 @@ template struct generic_product_impl : generic_product_impl {}; +template +struct generic_product_impl + : generic_product_impl {}; + +template +struct generic_product_impl + : generic_product_impl {}; + } // end namespace internal } // end namespace Eigen diff --git a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/functors/BinaryFunctors.h b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/functors/BinaryFunctors.h index b6ecfb5d55..85e1584ea8 100644 --- a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/functors/BinaryFunctors.h +++ b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/functors/BinaryFunctors.h @@ -207,20 +207,9 @@ struct functor_traits -struct typed_cmp_helper { - static constexpr bool SameType = is_same::value; - static constexpr bool IsNumeric = is_arithmetic::Real>::value; - static constexpr bool UseTyped = UseTypedComparators && SameType && IsNumeric; - using type = typename conditional::type; -}; - -template -using cmp_return_t = typename typed_cmp_helper::type; - template struct scalar_cmp_op : binary_op_base { - using result_type = cmp_return_t; + using result_type = std::conditional_t; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const { return a == b ? result_type(1) : result_type(0); } @@ -233,7 +222,7 @@ struct scalar_cmp_op : binary template struct scalar_cmp_op : binary_op_base { - using result_type = cmp_return_t; + using result_type = std::conditional_t; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const { return a < b ? result_type(1) : result_type(0); } @@ -246,7 +235,7 @@ struct scalar_cmp_op : binary template struct scalar_cmp_op : binary_op_base { - using result_type = cmp_return_t; + using result_type = std::conditional_t; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const { return a <= b ? result_type(1) : result_type(0); } @@ -259,7 +248,7 @@ struct scalar_cmp_op : binary template struct scalar_cmp_op : binary_op_base { - using result_type = cmp_return_t; + using result_type = std::conditional_t; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const { return a > b ? result_type(1) : result_type(0); } @@ -272,7 +261,7 @@ struct scalar_cmp_op : binary template struct scalar_cmp_op : binary_op_base { - using result_type = cmp_return_t; + using result_type = std::conditional_t; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const { return a >= b ? result_type(1) : result_type(0); } @@ -285,7 +274,7 @@ struct scalar_cmp_op : binary template struct scalar_cmp_op : binary_op_base { - using result_type = cmp_return_t; + using result_type = std::conditional_t; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const { return !(a <= b || b <= a) ? result_type(1) : result_type(0); } @@ -298,7 +287,7 @@ struct scalar_cmp_op : bin template struct scalar_cmp_op : binary_op_base { - using result_type = cmp_return_t; + using result_type = std::conditional_t; EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE result_type operator()(const LhsScalar& a, const RhsScalar& b) const { return a != b ? result_type(1) : result_type(0); } diff --git a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseCore/SparseMatrixBase.h b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseCore/SparseMatrixBase.h index fbf1313d54..ccbbe98da0 100644 --- a/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseCore/SparseMatrixBase.h +++ b/wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/SparseCore/SparseMatrixBase.h @@ -227,9 +227,6 @@ class SparseMatrixBase : public EigenBase { using Nested = typename Derived::Nested; using NestedCleaned = typename internal::remove_all::type; - /// For converting `0's` to the matrices numerical type - using Scalar = typename Derived::Scalar; - if (Flags & RowMajorBit) { Nested nm(m.derived()); internal::evaluator thisEval(nm); diff --git a/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java b/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java index d4262db4e1..83ff5b39fe 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java @@ -89,41 +89,166 @@ class MathUtilTest extends UtilityClassTest { } @Test - void testCopySignPow() { - assertEquals(0.5, MathUtil.copySignPow(0.5, 1.0)); - assertEquals(-0.5, MathUtil.copySignPow(-0.5, 1.0)); + void testApplyDeadband2dUnityScale() { + assertEquals( + VecBuilder.fill(0.0, 1.0), MathUtil.applyDeadband(VecBuilder.fill(0.0, 1.0), 0.02)); + assertEquals( + VecBuilder.fill(0.0, -1.0), MathUtil.applyDeadband(VecBuilder.fill(0.0, -1.0), 0.02)); + assertEquals( + VecBuilder.fill(-1.0, 0.0), MathUtil.applyDeadband(VecBuilder.fill(-1.0, 0.0), 0.02)); - assertEquals(0.5 * 0.5, MathUtil.copySignPow(0.5, 2.0)); - assertEquals(-(0.5 * 0.5), MathUtil.copySignPow(-0.5, 2.0)); + // == 0 + assertEquals( + VecBuilder.fill(0.0, 0.0), MathUtil.applyDeadband(VecBuilder.fill(0.0, 0.0), 0.02)); - assertEquals(Math.sqrt(0.5), MathUtil.copySignPow(0.5, 0.5)); - assertEquals(-Math.sqrt(0.5), MathUtil.copySignPow(-0.5, 0.5)); - - assertEquals(0.0, MathUtil.copySignPow(0.0, 2.0)); - assertEquals(1.0, MathUtil.copySignPow(1.0, 2.0)); - assertEquals(-1.0, MathUtil.copySignPow(-1.0, 2.0)); - - assertEquals(Math.pow(0.8, 0.3), MathUtil.copySignPow(0.8, 0.3)); - assertEquals(-Math.pow(0.8, 0.3), MathUtil.copySignPow(-0.8, 0.3)); + // > 0 + assertEquals( + VecBuilder.fill(0.0, 0.0), MathUtil.applyDeadband(VecBuilder.fill(0.01, 0.0), 0.02)); + assertEquals( + VecBuilder.fill(0.0, 0.0), MathUtil.applyDeadband(VecBuilder.fill(0.02, 0.0), 0.02)); + assertEquals( + VecBuilder.fill((0.03 - 0.02) / (1.0 - 0.02), 0.0), + MathUtil.applyDeadband(VecBuilder.fill(0.03, 0.0), 0.02)); + assertEquals( + VecBuilder.fill(1.0, 0.0), MathUtil.applyDeadband(VecBuilder.fill(1.0, 0.0), 0.02)); } @Test - void testCopySignPowMaxMagnitude() { - assertEquals(5, MathUtil.copySignPow(5.0, 1.0, 10.0)); - assertEquals(-5, MathUtil.copySignPow(-5.0, 1.0, 10.0)); + void testApplyDeadband2dArbitraryScale() { + assertEquals( + VecBuilder.fill(0.0, 2.5), MathUtil.applyDeadband(VecBuilder.fill(0.0, 2.5), 0.02, 2.5)); + assertEquals( + VecBuilder.fill(0.0, -2.5), MathUtil.applyDeadband(VecBuilder.fill(0.0, -2.5), 0.02, 2.5)); + assertEquals( + VecBuilder.fill(-2.5, 0.0), MathUtil.applyDeadband(VecBuilder.fill(-2.5, 0.0), 0.02, 2.5)); - assertEquals(0.5 * 0.5 * 10, MathUtil.copySignPow(5.0, 2.0, 10.0)); - assertEquals(-0.5 * 0.5 * 10, MathUtil.copySignPow(-5.0, 2.0, 10.0)); + // == 0 + assertEquals( + VecBuilder.fill(0.0, 0.0), MathUtil.applyDeadband(VecBuilder.fill(0.0, 0.0), 0.02, 2.5)); - assertEquals(Math.sqrt(0.5) * 10, MathUtil.copySignPow(5.0, 0.5, 10.0)); - assertEquals(-Math.sqrt(0.5) * 10, MathUtil.copySignPow(-5.0, 0.5, 10.0)); + // > 0 + assertEquals( + VecBuilder.fill(0.0, 0.0), MathUtil.applyDeadband(VecBuilder.fill(0.01, 0.0), 0.02, 2.5)); + assertEquals( + VecBuilder.fill(0.0, 0.0), MathUtil.applyDeadband(VecBuilder.fill(0.02, 0.0), 0.02, 2.5)); + assertEquals( + VecBuilder.fill(2.5, 0.0), MathUtil.applyDeadband(VecBuilder.fill(2.5, 0.0), 0.02, 2.5)); + } - assertEquals(0.0, MathUtil.copySignPow(0.0, 2.0, 5.0)); - assertEquals(5.0, MathUtil.copySignPow(5.0, 2.0, 5.0)); - assertEquals(-5.0, MathUtil.copySignPow(-5.0, 2.0, 5.0)); + @Test + void testApplyDeadband2dLargeMaxMagnitude() { + assertEquals( + VecBuilder.fill(80.0, 0.0), + MathUtil.applyDeadband(VecBuilder.fill(100.0, 0.0), 20, Double.POSITIVE_INFINITY)); + } - assertEquals(Math.pow(0.8, 0.3) * 100, MathUtil.copySignPow(80, 0.3, 100.0)); - assertEquals(-Math.pow(0.8, 0.3) * 100, MathUtil.copySignPow(-80, 0.3, 100.0)); + @Test + void testCopyDirectionPow() { + assertEquals(0.5, MathUtil.copyDirectionPow(0.5, 1.0)); + assertEquals(-0.5, MathUtil.copyDirectionPow(-0.5, 1.0)); + + assertEquals(0.5 * 0.5, MathUtil.copyDirectionPow(0.5, 2.0)); + assertEquals(-(0.5 * 0.5), MathUtil.copyDirectionPow(-0.5, 2.0)); + + assertEquals(Math.sqrt(0.5), MathUtil.copyDirectionPow(0.5, 0.5)); + assertEquals(-Math.sqrt(0.5), MathUtil.copyDirectionPow(-0.5, 0.5)); + + assertEquals(0.0, MathUtil.copyDirectionPow(0.0, 2.0)); + assertEquals(1.0, MathUtil.copyDirectionPow(1.0, 2.0)); + assertEquals(-1.0, MathUtil.copyDirectionPow(-1.0, 2.0)); + + assertEquals(Math.pow(0.8, 0.3), MathUtil.copyDirectionPow(0.8, 0.3)); + assertEquals(-Math.pow(0.8, 0.3), MathUtil.copyDirectionPow(-0.8, 0.3)); + } + + @Test + void testCopyDirectionPowMaxMagnitude() { + assertEquals(5, MathUtil.copyDirectionPow(5.0, 1.0, 10.0)); + assertEquals(-5, MathUtil.copyDirectionPow(-5.0, 1.0, 10.0)); + + assertEquals(0.5 * 0.5 * 10, MathUtil.copyDirectionPow(5.0, 2.0, 10.0)); + assertEquals(-0.5 * 0.5 * 10, MathUtil.copyDirectionPow(-5.0, 2.0, 10.0)); + + assertEquals(Math.sqrt(0.5) * 10, MathUtil.copyDirectionPow(5.0, 0.5, 10.0)); + assertEquals(-Math.sqrt(0.5) * 10, MathUtil.copyDirectionPow(-5.0, 0.5, 10.0)); + + assertEquals(0.0, MathUtil.copyDirectionPow(0.0, 2.0, 5.0)); + assertEquals(5.0, MathUtil.copyDirectionPow(5.0, 2.0, 5.0)); + assertEquals(-5.0, MathUtil.copyDirectionPow(-5.0, 2.0, 5.0)); + + assertEquals(Math.pow(0.8, 0.3) * 100, MathUtil.copyDirectionPow(80, 0.3, 100.0)); + assertEquals(-Math.pow(0.8, 0.3) * 100, MathUtil.copyDirectionPow(-80, 0.3, 100.0)); + } + + @Test + void testCopyDirectionPow2d() { + assertEquals( + VecBuilder.fill(0.5, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(0.5, 0.0), 1.0)); + assertEquals( + VecBuilder.fill(-0.5, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(-0.5, 0.0), 1.0)); + + assertEquals( + VecBuilder.fill(0.25, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(0.5, 0.0), 2.0)); + assertEquals( + VecBuilder.fill(-0.25, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(-0.5, 0.0), 2.0)); + + assertEquals( + VecBuilder.fill(Math.sqrt(0.5), 0.0), + MathUtil.copyDirectionPow(VecBuilder.fill(0.5, 0.0), 0.5)); + assertEquals( + VecBuilder.fill(-Math.sqrt(0.5), 0.0), + MathUtil.copyDirectionPow(VecBuilder.fill(-0.5, 0.0), 0.5)); + + assertEquals( + VecBuilder.fill(0.0, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(0.0, 0.0), 2.0)); + assertEquals( + VecBuilder.fill(1.0, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(1.0, 0.0), 2.0)); + assertEquals( + VecBuilder.fill(-1.0, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(-1.0, 0.0), 2.0)); + + assertEquals( + VecBuilder.fill(0.0, Math.pow(0.8, 0.3)), + MathUtil.copyDirectionPow(VecBuilder.fill(0.0, 0.8), 0.3)); + assertEquals( + VecBuilder.fill(0.0, -Math.pow(0.8, 0.3)), + MathUtil.copyDirectionPow(VecBuilder.fill(0.0, -0.8), 0.3)); + } + + @Test + void testCopyDirectionPow2dMaxDistance() { + assertEquals( + VecBuilder.fill(5.0, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(5.0, 0.0), 1.0, 10.0)); + assertEquals( + VecBuilder.fill(-5.0, 0.0), + MathUtil.copyDirectionPow(VecBuilder.fill(-5.0, 0.0), 1.0, 10.0)); + + assertEquals( + VecBuilder.fill(2.5, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(5.0, 0.0), 2.0, 10.0)); + assertEquals( + VecBuilder.fill(-2.5, 0.0), + MathUtil.copyDirectionPow(VecBuilder.fill(-5.0, 0.0), 2.0, 10.0)); + + assertEquals( + VecBuilder.fill(Math.sqrt(0.5) * 10, 0.0), + MathUtil.copyDirectionPow(VecBuilder.fill(5.0, 0.0), 0.5, 10.0)); + assertEquals( + VecBuilder.fill(-Math.sqrt(0.5) * 10, 0.0), + MathUtil.copyDirectionPow(VecBuilder.fill(-5.0, 0.0), 0.5, 10.0)); + + assertEquals( + VecBuilder.fill(0.0, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(0.0, 0.0), 2.0, 5.0)); + assertEquals( + VecBuilder.fill(5.0, 0.0), MathUtil.copyDirectionPow(VecBuilder.fill(5.0, 0.0), 2.0, 5.0)); + assertEquals( + VecBuilder.fill(-5.0, 0.0), + MathUtil.copyDirectionPow(VecBuilder.fill(-5.0, 0.0), 2.0, 5.0)); + + assertEquals( + VecBuilder.fill(0.0, Math.pow(0.8, 0.3) * 100), + MathUtil.copyDirectionPow(VecBuilder.fill(0.0, 80.0), 0.3, 100.0)); + assertEquals( + VecBuilder.fill(0.0, -Math.pow(0.8, 0.3) * 100), + MathUtil.copyDirectionPow(VecBuilder.fill(0.0, -80.0), 0.3, 100.0)); } @Test diff --git a/wpimath/src/test/native/cpp/MathUtilTest.cpp b/wpimath/src/test/native/cpp/MathUtilTest.cpp index 1479c0d18f..1222bc4073 100644 --- a/wpimath/src/test/native/cpp/MathUtilTest.cpp +++ b/wpimath/src/test/native/cpp/MathUtilTest.cpp @@ -55,8 +55,8 @@ TEST(MathUtilTest, ApplyDeadbandArbitraryScale) { TEST(MathUtilTest, ApplyDeadbandUnits) { // < 0 - EXPECT_DOUBLE_EQ( - -20, frc::ApplyDeadband(-20_rad, 1_rad, 20_rad).value()); + EXPECT_UNITS_EQ(-20_rad, + frc::ApplyDeadband(-20_rad, 1_rad, 20_rad)); } TEST(MathUtilTest, ApplyDeadbandLargeMaxMagnitude) { @@ -65,59 +65,213 @@ TEST(MathUtilTest, ApplyDeadbandLargeMaxMagnitude) { frc::ApplyDeadband(100.0, 20.0, std::numeric_limits::infinity())); } -TEST(MathUtilTest, CopySignPow) { - EXPECT_DOUBLE_EQ(0.5, frc::CopySignPow(0.5, 1.0)); - EXPECT_DOUBLE_EQ(-0.5, frc::CopySignPow(-0.5, 1.0)); +TEST(MathUtilTest, ApplyDeadband2dUnityScale) { + EXPECT_EQ((Eigen::Vector2d{{0.0}, {1.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.0}, {1.0}}, 0.02)); + EXPECT_EQ((Eigen::Vector2d{{0.0}, {-1.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.0}, {-1.0}}, 0.02)); + EXPECT_EQ((Eigen::Vector2d{{-1.0}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{-1.0}, {0.0}}, 0.02)); - EXPECT_DOUBLE_EQ(0.5 * 0.5, frc::CopySignPow(0.5, 2.0)); - EXPECT_DOUBLE_EQ(-(0.5 * 0.5), frc::CopySignPow(-0.5, 2.0)); + // == 0 + EXPECT_EQ((Eigen::Vector2d{{0.0}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.0}, {0.0}}, 0.02)); - EXPECT_DOUBLE_EQ(std::sqrt(0.5), frc::CopySignPow(0.5, 0.5)); - EXPECT_DOUBLE_EQ(-std::sqrt(0.5), frc::CopySignPow(-0.5, 0.5)); - - EXPECT_DOUBLE_EQ(0.0, frc::CopySignPow(0.0, 2.0)); - EXPECT_DOUBLE_EQ(1.0, frc::CopySignPow(1.0, 2.0)); - EXPECT_DOUBLE_EQ(-1.0, frc::CopySignPow(-1.0, 2.0)); - - EXPECT_DOUBLE_EQ(std::pow(0.8, 0.3), frc::CopySignPow(0.8, 0.3)); - EXPECT_DOUBLE_EQ(-std::pow(0.8, 0.3), frc::CopySignPow(-0.8, 0.3)); + // > 0 + EXPECT_EQ((Eigen::Vector2d{{0.0}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.01}, {0.0}}, 0.02)); + EXPECT_EQ((Eigen::Vector2d{{0.0}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.02}, {0.0}}, 0.02)); + EXPECT_EQ((Eigen::Vector2d{{(0.03 - 0.02) / (1.0 - 0.02)}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.03}, {0.0}}, 0.02)); + EXPECT_EQ((Eigen::Vector2d{{1.0}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{1.0}, {0.0}}, 0.02)); } -TEST(MathUtilTest, CopySignPowWithMaxMagnitude) { - EXPECT_DOUBLE_EQ(5.0, frc::CopySignPow(5.0, 1.0, 10.0)); - EXPECT_DOUBLE_EQ(-5.0, frc::CopySignPow(-5.0, 1.0, 10.0)); +TEST(MathUtilTest, ApplyDeadband2dArbitraryScale) { + EXPECT_EQ((Eigen::Vector2d{{0.0}, {2.5}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.0}, {2.5}}, 0.02, 2.5)); + EXPECT_EQ((Eigen::Vector2d{{0.0}, {-2.5}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.0}, {-2.5}}, 0.02, 2.5)); + EXPECT_EQ((Eigen::Vector2d{{-2.5}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{-2.5}, {0.0}}, 0.02, 2.5)); - EXPECT_DOUBLE_EQ(0.5 * 0.5 * 10, frc::CopySignPow(5.0, 2.0, 10.0)); - EXPECT_DOUBLE_EQ(-0.5 * 0.5 * 10, frc::CopySignPow(-5.0, 2.0, 10.0)); + // == 0 + EXPECT_EQ((Eigen::Vector2d{{0.0}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.0}, {0.0}}, 0.02, 2.5)); - EXPECT_DOUBLE_EQ(std::sqrt(0.5) * 10, frc::CopySignPow(5.0, 0.5, 10.0)); - EXPECT_DOUBLE_EQ(-std::sqrt(0.5) * 10, frc::CopySignPow(-5.0, 0.5, 10.0)); + // > 0 + EXPECT_EQ((Eigen::Vector2d{{0.0}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.01}, {0.0}}, 0.02, 2.5)); + EXPECT_EQ((Eigen::Vector2d{{0.0}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{0.02}, {0.0}}, 0.02, 2.5)); + EXPECT_EQ((Eigen::Vector2d{{2.5}, {0.0}}), + frc::ApplyDeadband(Eigen::Vector2d{{2.5}, {0.0}}, 0.02, 2.5)); +} - EXPECT_DOUBLE_EQ(0.0, frc::CopySignPow(0.0, 2.0, 5.0)); - EXPECT_DOUBLE_EQ(5.0, frc::CopySignPow(5.0, 2.0, 5.0)); - EXPECT_DOUBLE_EQ(-5.0, frc::CopySignPow(-5.0, 2.0, 5.0)); +TEST(MathUtilTest, ApplyDeadband2dLargeMaxMagnitude) { + EXPECT_EQ((Eigen::Vector2d{{80.0}, {0.0}}), + (frc::ApplyDeadband(Eigen::Vector2d{{100.0}, {0.0}}, 20.0, + std::numeric_limits::infinity()))); +} + +TEST(MathUtilTest, ApplyDeadband2dUnits) { + EXPECT_EQ((Eigen::Vector{0_mps, 2.5_mps}), + frc::ApplyDeadband( + Eigen::Vector{0_mps, 2.5_mps}, + 0.02_mps, 2.5_mps)); + EXPECT_EQ((Eigen::Vector{1_mps, 0_mps}), + frc::ApplyDeadband( + Eigen::Vector{1_mps, 0_mps}, + 0.02_mps)); + + EXPECT_EQ((Eigen::Vector{0_mps, 0_mps}), + frc::ApplyDeadband( + Eigen::Vector{0_mps, 0_mps}, + 0.02_mps, 2.5_mps)); +} + +TEST(MathUtilTest, CopyDirectionPow) { + EXPECT_DOUBLE_EQ(0.5, frc::CopyDirectionPow(0.5, 1.0)); + EXPECT_DOUBLE_EQ(-0.5, frc::CopyDirectionPow(-0.5, 1.0)); + + EXPECT_DOUBLE_EQ(0.5 * 0.5, frc::CopyDirectionPow(0.5, 2.0)); + EXPECT_DOUBLE_EQ(-(0.5 * 0.5), frc::CopyDirectionPow(-0.5, 2.0)); + + EXPECT_DOUBLE_EQ(std::sqrt(0.5), frc::CopyDirectionPow(0.5, 0.5)); + EXPECT_DOUBLE_EQ(-std::sqrt(0.5), frc::CopyDirectionPow(-0.5, 0.5)); + + EXPECT_DOUBLE_EQ(0.0, frc::CopyDirectionPow(0.0, 2.0)); + EXPECT_DOUBLE_EQ(1.0, frc::CopyDirectionPow(1.0, 2.0)); + EXPECT_DOUBLE_EQ(-1.0, frc::CopyDirectionPow(-1.0, 2.0)); + + EXPECT_DOUBLE_EQ(std::pow(0.8, 0.3), frc::CopyDirectionPow(0.8, 0.3)); + EXPECT_DOUBLE_EQ(-std::pow(0.8, 0.3), frc::CopyDirectionPow(-0.8, 0.3)); +} + +TEST(MathUtilTest, CopyDirectionPowWithMaxMagnitude) { + EXPECT_DOUBLE_EQ(5.0, frc::CopyDirectionPow(5.0, 1.0, 10.0)); + EXPECT_DOUBLE_EQ(-5.0, frc::CopyDirectionPow(-5.0, 1.0, 10.0)); + + EXPECT_DOUBLE_EQ(0.5 * 0.5 * 10, frc::CopyDirectionPow(5.0, 2.0, 10.0)); + EXPECT_DOUBLE_EQ(-0.5 * 0.5 * 10, frc::CopyDirectionPow(-5.0, 2.0, 10.0)); + + EXPECT_DOUBLE_EQ(std::sqrt(0.5) * 10, frc::CopyDirectionPow(5.0, 0.5, 10.0)); + EXPECT_DOUBLE_EQ(-std::sqrt(0.5) * 10, + frc::CopyDirectionPow(-5.0, 0.5, 10.0)); + + EXPECT_DOUBLE_EQ(0.0, frc::CopyDirectionPow(0.0, 2.0, 5.0)); + EXPECT_DOUBLE_EQ(5.0, frc::CopyDirectionPow(5.0, 2.0, 5.0)); + EXPECT_DOUBLE_EQ(-5.0, frc::CopyDirectionPow(-5.0, 2.0, 5.0)); EXPECT_DOUBLE_EQ(std::pow(0.8, 0.3) * 100, - frc::CopySignPow(80.0, 0.3, 100.0)); + frc::CopyDirectionPow(80.0, 0.3, 100.0)); EXPECT_DOUBLE_EQ(-std::pow(0.8, 0.3) * 100, - frc::CopySignPow(-80.0, 0.3, 100.0)); + frc::CopyDirectionPow(-80.0, 0.3, 100.0)); } -TEST(MathUtilTest, CopySignPowWithUnits) { - EXPECT_DOUBLE_EQ( - 0, frc::CopySignPow(0_mps, 2.0).value()); - EXPECT_DOUBLE_EQ( - 1, frc::CopySignPow(1_mps, 2.0).value()); - EXPECT_DOUBLE_EQ( - -1, frc::CopySignPow(-1_mps, 2.0).value()); +TEST(MathUtilTest, CopyDirectionPowWithUnits) { + EXPECT_UNITS_EQ( + 0_mps, frc::CopyDirectionPow(0_mps, 2.0)); + EXPECT_UNITS_EQ( + 1_mps, frc::CopyDirectionPow(1_mps, 2.0)); + EXPECT_UNITS_EQ( + -1_mps, frc::CopyDirectionPow(-1_mps, 2.0)); - EXPECT_DOUBLE_EQ( - 0.5 * 0.5 * 10, - frc::CopySignPow(5_mps, 2.0, 10_mps).value()); - EXPECT_DOUBLE_EQ( - -0.5 * 0.5 * 10, - frc::CopySignPow(-5_mps, 2.0, 10_mps) - .value()); + EXPECT_UNITS_EQ( + units::meters_per_second_t{0.5 * 0.5 * 10}, + frc::CopyDirectionPow(5_mps, 2.0, 10_mps)); + EXPECT_UNITS_EQ( + units::meters_per_second_t{-0.5 * 0.5 * 10}, + frc::CopyDirectionPow(-5_mps, 2.0, 10_mps)); +} + +TEST(MathUtilTest, CopyDirectionPow2d) { + EXPECT_EQ((Eigen::Vector2d{{0.5}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.5}, {0.0}}, 1.0)); + EXPECT_EQ((Eigen::Vector2d{{-0.5}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{-0.5}, {0.0}}, 1.0)); + + EXPECT_EQ((Eigen::Vector2d{{0.25}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.5}, {0.0}}, 2.0)); + EXPECT_EQ((Eigen::Vector2d{{-0.25}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{-0.5}, {0.0}}, 2.0)); + + EXPECT_EQ((Eigen::Vector2d{{std::sqrt(0.5)}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.5}, {0.0}}, 0.5)); + EXPECT_EQ((Eigen::Vector2d{{-std::sqrt(0.5)}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{-0.5}, {0.0}}, 0.5)); + + EXPECT_EQ((Eigen::Vector2d{{0.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.0}, {0.0}}, 2.0)); + + EXPECT_EQ((Eigen::Vector2d{{1.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{1.0}, {0.0}}, 2.0)); + EXPECT_EQ((Eigen::Vector2d{{-1.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{-1.0}, {0.0}}, 2.0)); + EXPECT_EQ((Eigen::Vector2d{{0.0}, {1.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.0}, {1.0}}, 2.0)); + EXPECT_EQ((Eigen::Vector2d{{0.0}, {-1.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.0}, {-1.0}}, 2.0)); + + EXPECT_EQ((Eigen::Vector2d{{0.0}, {std::pow(0.8, 0.3)}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.0}, {0.8}}, 0.3)); + EXPECT_EQ((Eigen::Vector2d{{0.0}, {-std::pow(0.8, 0.3)}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.0}, {-0.8}}, 0.3)); +} + +TEST(MathUtilTest, CopyDirectionPow2dMaxDistance) { + EXPECT_EQ((Eigen::Vector2d{{5.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{5.0}, {0.0}}, 1.0, 10.0)); + EXPECT_EQ((Eigen::Vector2d{{-5.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{-5.0}, {0.0}}, 1.0, 10.0)); + + EXPECT_EQ((Eigen::Vector2d{{2.5}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{5.0}, {0.0}}, 2.0, 10.0)); + EXPECT_EQ((Eigen::Vector2d{{-2.5}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{-5.0}, {0.0}}, 2.0, 10.0)); + + EXPECT_EQ((Eigen::Vector2d{{std::sqrt(0.5) * 10.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{5.0}, {0.0}}, 0.5, 10.0)); + EXPECT_EQ((Eigen::Vector2d{{-std::sqrt(0.5) * 10.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{-5.0}, {0.0}}, 0.5, 10.0)); + + EXPECT_EQ((Eigen::Vector2d{{0.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.0}, {0.0}}, 2.0, 5.0)); + EXPECT_EQ((Eigen::Vector2d{{5.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{5.0}, {0.0}}, 2.0, 5.0)); + EXPECT_EQ((Eigen::Vector2d{{-5.0}, {0.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{-5.0}, {0.0}}, 2.0, 5.0)); + + EXPECT_EQ((Eigen::Vector2d{{0.0}, {std::pow(0.8, 0.3) * 100.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.0}, {80.0}}, 0.3, 100.0)); + EXPECT_EQ((Eigen::Vector2d{{0.0}, {-std::pow(0.8, 0.3) * 100.0}}), + frc::CopyDirectionPow(Eigen::Vector2d{{0.0}, {-80.0}}, 0.3, 100.0)); +} + +TEST(MathUtilTest, CopyDirectionPow2dUnits) { + EXPECT_EQ( + (Eigen::Vector{1_mps, 0_mps}), + frc::CopyDirectionPow( + Eigen::Vector{1_mps, 0_mps}, 2.0)); + EXPECT_EQ( + (Eigen::Vector{-1_mps, 0_mps}), + frc::CopyDirectionPow( + Eigen::Vector{-1_mps, 0_mps}, 2.0)); + + EXPECT_EQ((Eigen::Vector{0_mps, 0_mps}), + frc::CopyDirectionPow( + Eigen::Vector{0_mps, 0_mps}, 2.0, + 5_mps)); + + EXPECT_EQ((Eigen::Vector{5_mps, 0_mps}), + frc::CopyDirectionPow( + Eigen::Vector{5_mps, 0_mps}, 2.0, + 5_mps)); + EXPECT_EQ((Eigen::Vector{-5_mps, 0_mps}), + frc::CopyDirectionPow( + Eigen::Vector{-5_mps, 0_mps}, + 2.0, 5_mps)); } TEST(MathUtilTest, InputModulus) {