diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java index 356961aff9..36d570b886 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/Color.java @@ -328,9 +328,9 @@ public class Color { */ public static int lerpRGB(int r1, int g1, int b1, int r2, int g2, int b2, double t) { return packRGB( - (int) MathUtil.interpolate(r1, r2, t), - (int) MathUtil.interpolate(g1, g2, t), - (int) MathUtil.interpolate(b1, b2, t)); + (int) MathUtil.lerp(r1, r2, t), + (int) MathUtil.lerp(g1, g2, t), + (int) MathUtil.lerp(b1, b2, t)); } /* 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 43b4408fc5..4004ab4d2a 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java +++ b/wpimath/src/main/java/edu/wpi/first/math/MathUtil.java @@ -13,6 +13,39 @@ public final class MathUtil { throw new AssertionError("utility class"); } + /** + * Computes the linear interpolation between a and b if t ∈ [0, 1) and the linear extrapolation + * otherwise. + * + * @param a The start value. + * @param b The end value. + * @param t The fraction for interpolation. + * @return The interpolated value. + */ + public static double lerp(double a, double b, double t) { + return a + (b - a) * t; + } + + /** + * Returns the interpolant t that reflects where q is with respect to the range (a, b). In other + * words, returns t such that q = a + (b - a)t. If a = b, then returns 0. + * + * @param a Lower part of interpolation range. + * @param b Upper part of interpolation range. + * @param q Query. + * @return Interpolant. + */ + public static double inverseLerp(double a, double b, double q) { + // q = a + (b − a)t + // (b − a)t = q − a + // t = (q − a)/(b − a) + if (Math.abs(a - b) < 1e-9) { + return 0.0; + } else { + return (q - a) / (b - a); + } + } + /** * Returns 0.0 if the given value is within the specified range around zero. The remaining range * between the deadband and the maximum magnitude is scaled from 0.0 to the maximum magnitude. @@ -151,38 +184,6 @@ public final class MathUtil { return inputModulus(angleRadians, -Math.PI, Math.PI); } - /** - * Perform linear interpolation between two values. - * - * @param startValue The value to start at. - * @param endValue The value to end at. - * @param t How far between the two values to interpolate. This is clamped to [0, 1]. - * @return The interpolated value. - */ - public static double interpolate(double startValue, double endValue, double t) { - return startValue + (endValue - startValue) * Math.clamp(t, 0, 1); - } - - /** - * Return where within interpolation range [0, 1] q is between startValue and endValue. - * - * @param startValue Lower part of interpolation range. - * @param endValue Upper part of interpolation range. - * @param q Query. - * @return Interpolant in range [0, 1]. - */ - public static double inverseInterpolate(double startValue, double endValue, double q) { - double totalRange = endValue - startValue; - if (totalRange <= 0) { - return 0.0; - } - double queryToStart = q - startValue; - if (queryToStart <= 0) { - return 0.0; - } - return queryToStart / totalRange; - } - /** * Checks if the given value matches an expected value within a certain tolerance. * diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java index 10c7c123bd..767c3b81a0 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation2d.java @@ -358,8 +358,8 @@ public class Translation2d @Override public Translation2d interpolate(Translation2d endValue, double t) { return new Translation2d( - MathUtil.interpolate(this.getX(), endValue.getX(), t), - MathUtil.interpolate(this.getY(), endValue.getY(), t)); + MathUtil.lerp(this.getX(), endValue.getX(), t), + MathUtil.lerp(this.getY(), endValue.getY(), t)); } /** Translation2d protobuf for serialization. */ diff --git a/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java index ff7eb01de6..3a4bc5fad2 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java +++ b/wpimath/src/main/java/edu/wpi/first/math/geometry/Translation3d.java @@ -393,9 +393,9 @@ public class Translation3d @Override public Translation3d interpolate(Translation3d endValue, double t) { return new Translation3d( - MathUtil.interpolate(this.getX(), endValue.getX(), t), - MathUtil.interpolate(this.getY(), endValue.getY(), t), - MathUtil.interpolate(this.getZ(), endValue.getZ(), t)); + MathUtil.lerp(this.getX(), endValue.getX(), t), + MathUtil.lerp(this.getY(), endValue.getY(), t), + MathUtil.lerp(this.getZ(), endValue.getZ(), t)); } /** Translation3d protobuf for serialization. */ diff --git a/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java b/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java index 5fe1a8f28a..37192cf9ec 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/interpolation/Interpolator.java @@ -30,6 +30,6 @@ public interface Interpolator { * @return Interpolator for Double. */ static Interpolator forDouble() { - return MathUtil::interpolate; + return MathUtil::lerp; } } diff --git a/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java b/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java index d6d0bc8c5b..92f33e7de9 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java +++ b/wpimath/src/main/java/edu/wpi/first/math/interpolation/InverseInterpolator.java @@ -30,6 +30,6 @@ public interface InverseInterpolator { * @return Inverse interpolator for Double. */ static InverseInterpolator forDouble() { - return MathUtil::inverseInterpolate; + return MathUtil::inverseLerp; } } diff --git a/wpimath/src/main/java/edu/wpi/first/math/interpolation/TimeInterpolatableBuffer.java b/wpimath/src/main/java/edu/wpi/first/math/interpolation/TimeInterpolatableBuffer.java index 3882583816..3d4c4b8a13 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/interpolation/TimeInterpolatableBuffer.java +++ b/wpimath/src/main/java/edu/wpi/first/math/interpolation/TimeInterpolatableBuffer.java @@ -65,7 +65,7 @@ public final class TimeInterpolatableBuffer { * @return The new TimeInterpolatableBuffer. */ public static TimeInterpolatableBuffer createDoubleBuffer(double historySize) { - return new TimeInterpolatableBuffer<>(MathUtil::interpolate, historySize); + return new TimeInterpolatableBuffer<>(MathUtil::lerp, historySize); } /** diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java index a91804059c..82b761ca0e 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/DifferentialDriveWheelPositions.java @@ -77,7 +77,6 @@ public class DifferentialDriveWheelPositions public DifferentialDriveWheelPositions interpolate( DifferentialDriveWheelPositions endValue, double t) { return new DifferentialDriveWheelPositions( - MathUtil.interpolate(this.left, endValue.left, t), - MathUtil.interpolate(this.right, endValue.right, t)); + MathUtil.lerp(this.left, endValue.left, t), MathUtil.lerp(this.right, endValue.right, t)); } } diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java index 4faeafc69c..663c8a9e77 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/MecanumDriveWheelPositions.java @@ -96,9 +96,9 @@ public class MecanumDriveWheelPositions @Override public MecanumDriveWheelPositions interpolate(MecanumDriveWheelPositions endValue, double t) { return new MecanumDriveWheelPositions( - MathUtil.interpolate(this.frontLeft, endValue.frontLeft, t), - MathUtil.interpolate(this.frontRight, endValue.frontRight, t), - MathUtil.interpolate(this.rearLeft, endValue.rearLeft, t), - MathUtil.interpolate(this.rearRight, endValue.rearRight, t)); + MathUtil.lerp(this.frontLeft, endValue.frontLeft, t), + MathUtil.lerp(this.frontRight, endValue.frontRight, t), + MathUtil.lerp(this.rearLeft, endValue.rearLeft, t), + MathUtil.lerp(this.rearRight, endValue.rearRight, t)); } } diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java index fc65e5f788..10ddb4aba7 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveModulePosition.java @@ -99,7 +99,7 @@ public class SwerveModulePosition @Override public SwerveModulePosition interpolate(SwerveModulePosition endValue, double t) { return new SwerveModulePosition( - MathUtil.interpolate(this.distance, endValue.distance, t), + MathUtil.lerp(this.distance, endValue.distance, t), this.angle.interpolate(endValue.angle, t)); } } diff --git a/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java b/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java index bea2261feb..df4b30bbda 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java +++ b/wpimath/src/main/java/edu/wpi/first/math/trajectory/Trajectory.java @@ -5,6 +5,7 @@ package edu.wpi.first.math.trajectory; import com.fasterxml.jackson.annotation.JsonProperty; +import edu.wpi.first.math.MathUtil; import edu.wpi.first.math.geometry.Pose2d; import edu.wpi.first.math.geometry.Transform2d; import edu.wpi.first.math.trajectory.proto.TrajectoryProto; @@ -48,18 +49,6 @@ public class Trajectory implements ProtobufSerializable { m_totalTime = m_states.get(m_states.size() - 1).time; } - /** - * Linearly interpolates between two values. - * - * @param startValue The start value. - * @param endValue The end value. - * @param t The fraction for interpolation. - * @return The interpolated value. - */ - private static double lerp(double startValue, double endValue, double t) { - return startValue + (endValue - startValue) * t; - } - /** * Linearly interpolates between two poses. * @@ -317,7 +306,7 @@ public class Trajectory implements ProtobufSerializable { */ State interpolate(State endValue, double i) { // Find the new t value. - final double newT = lerp(time, endValue.time, i); + final double newT = MathUtil.lerp(time, endValue.time, i); // Find the delta time between the current state and the interpolated state. final double deltaT = newT - time; @@ -351,7 +340,7 @@ public class Trajectory implements ProtobufSerializable { newV, acceleration, lerp(pose, endValue.pose, interpolationFrac), - lerp(curvature, endValue.curvature, interpolationFrac)); + MathUtil.lerp(curvature, endValue.curvature, interpolationFrac)); } @Override 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 ed9f4ef8f5..d4262db4e1 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/MathUtilTest.java @@ -18,6 +18,37 @@ class MathUtilTest extends UtilityClassTest { super(MathUtil.class); } + @Test + void testLerp() { + // 0% + assertEquals(2.0, MathUtil.lerp(2.0, 3.0, 0.0)); + assertEquals(1.0, MathUtil.lerp(1.0, 5.0, 0.0)); + + // 25% + assertEquals(2.25, MathUtil.lerp(2.0, 3.0, 0.25)); + assertEquals(2.0, MathUtil.lerp(1.0, 5.0, 0.25)); + + // 50% + assertEquals(2.5, MathUtil.lerp(2.0, 3.0, 0.5)); + assertEquals(3.0, MathUtil.lerp(1.0, 5.0, 0.5)); + + // 75% + assertEquals(2.75, MathUtil.lerp(2.0, 3.0, 0.75)); + assertEquals(4.0, MathUtil.lerp(1.0, 5.0, 0.75)); + + // 100% + assertEquals(3.0, MathUtil.lerp(2.0, 3.0, 1.0)); + assertEquals(5.0, MathUtil.lerp(1.0, 5.0, 1.0)); + + // Below minimum + assertEquals(0.0, MathUtil.lerp(2.0, 3.0, -2.0)); + assertEquals(-7.0, MathUtil.lerp(1.0, 5.0, -2.0)); + + // Above maximum + assertEquals(4.0, MathUtil.lerp(2.0, 3.0, 2.0)); + assertEquals(9.0, MathUtil.lerp(1.0, 5.0, 2.0)); + } + @Test void testApplyDeadbandUnityScale() { // < 0 @@ -136,17 +167,6 @@ class MathUtilTest extends UtilityClassTest { assertEquals(MathUtil.angleModulus(-Math.PI / 2), -Math.PI / 2); } - @Test - void testInterpolate() { - assertEquals(50, MathUtil.interpolate(0, 100, 0.5)); - assertEquals(-50, MathUtil.interpolate(0, -100, 0.5)); - assertEquals(0, MathUtil.interpolate(-50, 50, 0.5)); - assertEquals(-25, MathUtil.interpolate(-50, 50, 0.25)); - assertEquals(25, MathUtil.interpolate(-50, 50, 0.75)); - - assertEquals(0, MathUtil.interpolate(0, -100, -0.5)); - } - @Test void testIsNear() { // The answer is always 42