Skip to content

Commit 7a2a982

Browse files
committed
Merge branch 'main' into 2027
2 parents b37e2d9 + 9e85f3c commit 7a2a982

File tree

14 files changed

+543
-122
lines changed

14 files changed

+543
-122
lines changed

CODE_OF_CONDUCT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ the consequences for any action they deem in violation of this Code of Conduct:
5656
**Community Impact**: Use of inappropriate language or other behavior deemed
5757
unprofessional or unwelcome in the community.
5858

59-
**Consequence**: A private, written warning from community leaders, providing
59+
**Consequence**: A warning from community leaders, providing
6060
clarity around the nature of the violation and an explanation of why the
6161
behavior was inappropriate. A public apology may be requested.
6262

docs/build.gradle

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,16 @@ doxygen {
5353
// See below maven and https://doxygen.nl/download.html for provided binaries
5454
// Ensure theme.css (from https://github.com/jothepro/doxygen-awesome-css) is compatible with
5555
// doxygen version when updating
56-
57-
String arch = System.getProperty("os.arch");
58-
if (arch.equals("x86_64") || arch.equals("amd64")) {
59-
executables {
60-
doxygen {
61-
executableByVersion('1.12.0')
56+
executables {
57+
doxygen {
58+
// Note: has no effect if not on an x86_64 platform - you need to have a global install available on your
59+
// PATH for the doxygen plugin to run
60+
executableByVersion('1.12.0')
61+
62+
String arch = System.getProperty("os.arch");
63+
if (!(arch.equals("x86_64") || arch.equals("amd64"))) {
64+
// Search for a local doxygen install
65+
executableBySearchPath('doxygen')
6266
}
6367
}
6468
}

upstream_utils/eigen.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,7 @@ def copy_upstream_src(wpilib_root: Path):
154154
def main():
155155
name = "eigen"
156156
url = "https://gitlab.com/libeigen/eigen.git"
157-
# 5.0.0 release as of 2025-09-23
158-
tag = "d65cda87c1a673047b59b20a9f9e165a452f91e9"
157+
tag = "5.0.0"
159158

160159
eigen = Lib(name, url, tag, copy_upstream_src)
161160
eigen.main()

wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ DifferentialDrive::WheelSpeeds DifferentialDrive::ArcadeDriveIK(
112112
// Square the inputs (while preserving the sign) to increase fine control
113113
// while permitting full power.
114114
if (squareInputs) {
115-
xSpeed = CopySignPow(xSpeed, 2);
116-
zRotation = CopySignPow(zRotation, 2);
115+
xSpeed = CopyDirectionPow(xSpeed, 2);
116+
zRotation = CopyDirectionPow(zRotation, 2);
117117
}
118118

119119
double leftSpeed = xSpeed - zRotation;
@@ -167,8 +167,8 @@ DifferentialDrive::WheelSpeeds DifferentialDrive::TankDriveIK(
167167
// Square the inputs (while preserving the sign) to increase fine control
168168
// while permitting full power.
169169
if (squareInputs) {
170-
leftSpeed = CopySignPow(leftSpeed, 2);
171-
rightSpeed = CopySignPow(rightSpeed, 2);
170+
leftSpeed = CopyDirectionPow(leftSpeed, 2);
171+
rightSpeed = CopyDirectionPow(rightSpeed, 2);
172172
}
173173

174174
return {leftSpeed, rightSpeed};

wpilibj/src/main/java/edu/wpi/first/wpilibj/drive/DifferentialDrive.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ public static WheelSpeeds arcadeDriveIK(double xSpeed, double zRotation, boolean
260260
// Square the inputs (while preserving the sign) to increase fine control
261261
// while permitting full power.
262262
if (squareInputs) {
263-
xSpeed = MathUtil.copySignPow(xSpeed, 2);
264-
zRotation = MathUtil.copySignPow(zRotation, 2);
263+
xSpeed = MathUtil.copyDirectionPow(xSpeed, 2);
264+
zRotation = MathUtil.copyDirectionPow(zRotation, 2);
265265
}
266266

267267
double leftSpeed = xSpeed - zRotation;
@@ -335,8 +335,8 @@ public static WheelSpeeds tankDriveIK(double leftSpeed, double rightSpeed, boole
335335
// Square the inputs (while preserving the sign) to increase fine control
336336
// while permitting full power.
337337
if (squareInputs) {
338-
leftSpeed = MathUtil.copySignPow(leftSpeed, 2);
339-
rightSpeed = MathUtil.copySignPow(rightSpeed, 2);
338+
leftSpeed = MathUtil.copyDirectionPow(leftSpeed, 2);
339+
rightSpeed = MathUtil.copyDirectionPow(rightSpeed, 2);
340340
}
341341

342342
return new WheelSpeeds(leftSpeed, rightSpeed);

wpimath/src/main/java/edu/wpi/first/math/MathUtil.java

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,39 @@ public static double applyDeadband(double value, double deadband) {
116116
return applyDeadband(value, deadband, 1);
117117
}
118118

119+
/**
120+
* Returns a zero vector if the given vector is within the specified distance from the origin. The
121+
* remaining distance between the deadband and the maximum distance is scaled from the origin to
122+
* the maximum distance.
123+
*
124+
* @param value Value to clip.
125+
* @param deadband Distance from origin.
126+
* @param maxMagnitude The maximum distance from the origin of the input. Can be infinite.
127+
* @param <R> The number of rows in the vector.
128+
* @return The value after the deadband is applied.
129+
*/
130+
public static <R extends Num> Vector<R> applyDeadband(
131+
Vector<R> value, double deadband, double maxMagnitude) {
132+
if (value.norm() < 1e-9) {
133+
return value.times(0);
134+
}
135+
return value.unit().times(applyDeadband(value.norm(), deadband, maxMagnitude));
136+
}
137+
138+
/**
139+
* Returns a zero vector if the given vector is within the specified distance from the origin. The
140+
* remaining distance between the deadband and a distance of 1.0 is scaled from the origin to a
141+
* distance of 1.0.
142+
*
143+
* @param value Value to clip.
144+
* @param deadband Distance from origin.
145+
* @param <R> The number of rows in the vector.
146+
* @return The value after the deadband is applied.
147+
*/
148+
public static <R extends Num> Vector<R> applyDeadband(Vector<R> value, double deadband) {
149+
return applyDeadband(value, deadband, 1);
150+
}
151+
119152
/**
120153
* Raises the input to the power of the given exponent while preserving its sign.
121154
*
@@ -133,7 +166,7 @@ public static double applyDeadband(double value, double deadband) {
133166
* @param maxMagnitude The maximum expected absolute value of input. Must be positive.
134167
* @return The transformed value with the same sign and scaled to the input range.
135168
*/
136-
public static double copySignPow(double value, double exponent, double maxMagnitude) {
169+
public static double copyDirectionPow(double value, double exponent, double maxMagnitude) {
137170
return Math.copySign(Math.pow(Math.abs(value) / maxMagnitude, exponent), value) * maxMagnitude;
138171
}
139172

@@ -148,8 +181,44 @@ public static double copySignPow(double value, double exponent, double maxMagnit
148181
* positive.
149182
* @return The transformed value with the same sign.
150183
*/
151-
public static double copySignPow(double value, double exponent) {
152-
return copySignPow(value, exponent, 1);
184+
public static double copyDirectionPow(double value, double exponent) {
185+
return copyDirectionPow(value, exponent, 1);
186+
}
187+
188+
/**
189+
* Raises the norm of the input to the power of the given exponent while preserving its direction.
190+
*
191+
* <p>The function normalizes the norm of the input to the range [0, 1] based on the maximum
192+
* distance, raises it to the power of the exponent, then scales the result back to the original
193+
* range. This keeps the value in the original max distance and gives consistent curve behavior
194+
* regardless of the input norm's scale.
195+
*
196+
* @param value The input vector to transform.
197+
* @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared curve). Must be
198+
* positive.
199+
* @param maxMagnitude The maximum expected distance from origin of input. Must be positive.
200+
* @param <R> The number of rows in the vector.
201+
* @return The transformed value with the same direction and norm scaled to the input range.
202+
*/
203+
public static <R extends Num> Vector<R> copyDirectionPow(
204+
Vector<R> value, double exponent, double maxMagnitude) {
205+
if (value.norm() < 1e-9) {
206+
return value.times(0);
207+
}
208+
return value.unit().times(copyDirectionPow(value.norm(), exponent, maxMagnitude));
209+
}
210+
211+
/**
212+
* Raises the norm of the input to the power of the given exponent while preserving its direction.
213+
*
214+
* @param value The input vector to transform.
215+
* @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared curve). Must be
216+
* positive.
217+
* @param <R> The number of rows in the vector.
218+
* @return The transformed value with the same direction.
219+
*/
220+
public static <R extends Num> Vector<R> copyDirectionPow(Vector<R> value, double exponent) {
221+
return copyDirectionPow(value, exponent, 1);
153222
}
154223

155224
/**

wpimath/src/main/native/include/frc/MathUtil.h

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,30 +94,57 @@ constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) {
9494
}
9595
}
9696

97+
/**
98+
* Returns a zero vector if the given vector is within the specified
99+
* distance from the origin. The remaining distance between the deadband and the
100+
* maximum distance is scaled from the origin to the maximum distance.
101+
*
102+
* @param value Value to clip.
103+
* @param deadband Distance from origin.
104+
* @param maxMagnitude The maximum distance from the origin of the input
105+
* (defaults to 1). Can be infinite.
106+
* @return The value after the deadband is applied.
107+
*/
108+
template <typename T, int N>
109+
requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
110+
Eigen::Vector<T, N> ApplyDeadband(const Eigen::Vector<T, N>& value, T deadband,
111+
T maxMagnitude = T{1.0}) {
112+
if constexpr (std::is_arithmetic_v<T>) {
113+
if (value.norm() < T{1e-9}) {
114+
return Eigen::Vector<T, N>::Zero();
115+
}
116+
return value.normalized() *
117+
ApplyDeadband(value.norm(), deadband, maxMagnitude);
118+
} else {
119+
const Eigen::Vector<double, N> asDouble = value.template cast<double>();
120+
const Eigen::Vector<double, N> processed =
121+
ApplyDeadband(asDouble, deadband.value(), maxMagnitude.value());
122+
return processed.template cast<T>();
123+
}
124+
}
125+
97126
/**
98127
* Raises the input to the power of the given exponent while preserving its
99128
* sign.
100129
*
101130
* The function normalizes the input value to the range [0, 1] based on the
102-
* maximum magnitude, raises it to the power of the exponent, then scales the
103-
* result back to the original range and copying the sign. This keeps the value
104-
* in the original range and gives consistent curve behavior regardless of the
105-
* input value's scale.
131+
* maximum magnitude so that the output stays in the range.
106132
*
107133
* This is useful for applying smoother or more aggressive control response
108134
* curves (e.g. joystick input shaping).
109135
*
110136
* @param value The input value to transform.
111137
* @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared
112138
* curve). Must be positive.
113-
* @param maxMagnitude The maximum expected absolute value of input. Must be
114-
* positive.
139+
* @param maxMagnitude The maximum expected absolute value of input (defaults to
140+
* 1). Must be positive.
115141
* @return The transformed value with the same sign and scaled to the input
116142
* range.
117143
*/
118144
template <typename T>
119145
requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
120-
constexpr T CopySignPow(T value, double exponent, T maxMagnitude = T{1.0}) {
146+
constexpr T CopyDirectionPow(T value, double exponent,
147+
T maxMagnitude = T{1.0}) {
121148
if constexpr (std::is_arithmetic_v<T>) {
122149
return gcem::copysign(
123150
gcem::pow(gcem::abs(value) / maxMagnitude, exponent) * maxMagnitude,
@@ -130,6 +157,42 @@ constexpr T CopySignPow(T value, double exponent, T maxMagnitude = T{1.0}) {
130157
}
131158
}
132159

160+
/**
161+
* Raises the norm of the input to the power of the given exponent while
162+
* preserving its direction.
163+
*
164+
* The function normalizes the input value to the range [0, 1] based on the
165+
* maximum magnitude so that the output stays in the range.
166+
*
167+
* This is useful for applying smoother or more aggressive control response
168+
* curves (e.g. joystick input shaping).
169+
*
170+
* @param value The input vector to transform.
171+
* @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared
172+
* curve). Must be positive.
173+
* @param maxMagnitude The maximum expected distance from origin of input
174+
* (defaults to 1). Must be positive.
175+
* @return The transformed value with the same direction and norm scaled to
176+
* the input range.
177+
*/
178+
template <typename T, int N>
179+
requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
180+
Eigen::Vector<T, N> CopyDirectionPow(const Eigen::Vector<T, N>& value,
181+
double exponent, T maxMagnitude = T{1.0}) {
182+
if constexpr (std::is_arithmetic_v<T>) {
183+
if (value.norm() < T{1e-9}) {
184+
return Eigen::Vector<T, N>::Zero();
185+
}
186+
return value.normalized() *
187+
CopyDirectionPow(value.norm(), exponent, maxMagnitude);
188+
} else {
189+
const Eigen::Vector<double, N> asDouble = value.template cast<double>();
190+
const Eigen::Vector<double, N> processed =
191+
CopyDirectionPow(asDouble, exponent, maxMagnitude.value());
192+
return processed.template cast<T>();
193+
}
194+
}
195+
133196
/**
134197
* Returns modulus of input.
135198
*
@@ -279,6 +342,7 @@ constexpr Translation2d SlewRateLimit(const Translation2d& current,
279342
}
280343
if (dist > maxVelocity * dt) {
281344
// Move maximum allowed amount in direction of the difference
345+
// NOLINTNEXTLINE(bugprone-integer-division)
282346
return current + diff * (maxVelocity * dt / dist);
283347
}
284348
return next;
@@ -309,6 +373,7 @@ constexpr Translation3d SlewRateLimit(const Translation3d& current,
309373
}
310374
if (dist > maxVelocity * dt) {
311375
// Move maximum allowed amount in direction of the difference
376+
// NOLINTNEXTLINE(bugprone-integer-division)
312377
return current + diff * (maxVelocity * dt / dist);
313378
}
314379
return next;

wpimath/src/main/native/thirdparty/eigen/include/Eigen/Version

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
#define EIGEN_MAJOR_VERSION 5
88
#define EIGEN_MINOR_VERSION 0
99
#define EIGEN_PATCH_VERSION 0
10-
#define EIGEN_PRERELEASE_VERSION
11-
#define EIGEN_BUILD_VERSION
10+
#define EIGEN_PRERELEASE_VERSION ""
11+
#define EIGEN_BUILD_VERSION ""
1212
#define EIGEN_VERSION_STRING "5.0.0"
1313

1414
#endif // EIGEN_VERSION_H

wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/CoreEvaluators.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,22 +1048,33 @@ struct ternary_evaluator<CwiseTernaryOp<TernaryOp, Arg1, Arg2, Arg3>, IndexBased
10481048
Data m_d;
10491049
};
10501050

1051-
// specialization for expressions like (a < b).select(c, d) to enable full vectorization
10521051
template <typename Arg1, typename Arg2, typename Scalar, typename CmpLhsType, typename CmpRhsType, ComparisonName cmp>
1053-
struct evaluator<CwiseTernaryOp<scalar_boolean_select_op<Scalar, Scalar, bool>, Arg1, Arg2,
1054-
CwiseBinaryOp<scalar_cmp_op<Scalar, Scalar, cmp, false>, CmpLhsType, CmpRhsType>>>
1055-
: public ternary_evaluator<
1056-
CwiseTernaryOp<scalar_boolean_select_op<Scalar, Scalar, Scalar>, Arg1, Arg2,
1057-
CwiseBinaryOp<scalar_cmp_op<Scalar, Scalar, cmp, true>, CmpLhsType, CmpRhsType>>> {
1052+
struct scalar_boolean_select_spec {
10581053
using DummyTernaryOp = scalar_boolean_select_op<Scalar, Scalar, bool>;
10591054
using DummyArg3 = CwiseBinaryOp<scalar_cmp_op<Scalar, Scalar, cmp, false>, CmpLhsType, CmpRhsType>;
10601055
using DummyXprType = CwiseTernaryOp<DummyTernaryOp, Arg1, Arg2, DummyArg3>;
10611056

1062-
using TernaryOp = scalar_boolean_select_op<Scalar, Scalar, Scalar>;
1063-
using Arg3 = CwiseBinaryOp<scalar_cmp_op<Scalar, Scalar, cmp, true>, CmpLhsType, CmpRhsType>;
1057+
// only use the typed comparison if it is vectorized
1058+
static constexpr bool UseTyped = functor_traits<scalar_cmp_op<Scalar, Scalar, cmp, true>>::PacketAccess;
1059+
using CondScalar = std::conditional_t<UseTyped, Scalar, bool>;
1060+
1061+
using TernaryOp = scalar_boolean_select_op<Scalar, Scalar, CondScalar>;
1062+
using Arg3 = CwiseBinaryOp<scalar_cmp_op<Scalar, Scalar, cmp, UseTyped>, CmpLhsType, CmpRhsType>;
10641063
using XprType = CwiseTernaryOp<TernaryOp, Arg1, Arg2, Arg3>;
10651064

10661065
using Base = ternary_evaluator<XprType>;
1066+
};
1067+
1068+
// specialization for expressions like (a < b).select(c, d) to enable full vectorization
1069+
template <typename Arg1, typename Arg2, typename Scalar, typename CmpLhsType, typename CmpRhsType, ComparisonName cmp>
1070+
struct evaluator<CwiseTernaryOp<scalar_boolean_select_op<Scalar, Scalar, bool>, Arg1, Arg2,
1071+
CwiseBinaryOp<scalar_cmp_op<Scalar, Scalar, cmp, false>, CmpLhsType, CmpRhsType>>>
1072+
: public scalar_boolean_select_spec<Arg1, Arg2, Scalar, CmpLhsType, CmpRhsType, cmp>::Base {
1073+
using Helper = scalar_boolean_select_spec<Arg1, Arg2, Scalar, CmpLhsType, CmpRhsType, cmp>;
1074+
using Base = typename Helper::Base;
1075+
using DummyXprType = typename Helper::DummyXprType;
1076+
using Arg3 = typename Helper::Arg3;
1077+
using XprType = typename Helper::XprType;
10671078

10681079
EIGEN_DEVICE_FUNC explicit evaluator(const DummyXprType& xpr)
10691080
: Base(XprType(xpr.arg1(), xpr.arg2(), Arg3(xpr.arg3().lhs(), xpr.arg3().rhs()))) {}

wpimath/src/main/native/thirdparty/eigen/include/Eigen/src/Core/ProductEvaluators.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,14 @@ template <typename Lhs, typename Rhs, int ProductTag, typename MatrixShape>
12721272
struct generic_product_impl<Lhs, Rhs, HomogeneousShape, MatrixShape, ProductTag>
12731273
: generic_product_impl<typename Lhs::PlainObject, Rhs, DenseShape, MatrixShape, ProductTag> {};
12741274

1275+
template <typename Lhs, typename Rhs, int ProductTag>
1276+
struct generic_product_impl<Lhs, Rhs, PermutationShape, HomogeneousShape, ProductTag>
1277+
: generic_product_impl<Lhs, Rhs, PermutationShape, DenseShape, ProductTag> {};
1278+
1279+
template <typename Lhs, typename Rhs, int ProductTag>
1280+
struct generic_product_impl<Lhs, Rhs, HomogeneousShape, PermutationShape, ProductTag>
1281+
: generic_product_impl<Lhs, Rhs, DenseShape, PermutationShape, ProductTag> {};
1282+
12751283
} // end namespace internal
12761284

12771285
} // end namespace Eigen

0 commit comments

Comments
 (0)