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

feat(math/curves): implement abstract Curve class and multiple different curve types. #4

Merged
merged 13 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
1 change: 1 addition & 0 deletions build.gradle
Trip-kun marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies {
compileOnly "edu.wpi.first.wpilibj:wpilibj-java:2024.3.2"
compileOnly "edu.wpi.first.wpiutil:wpiutil-java:2024.3.2"
compileOnly "edu.wpi.first.wpilibj:commands:2024.3.2"
compileOnly "edu.wpi.first.wpimath:wpimath-java:2024.3.2"
compileOnly "edu.wpi.first.wpilibNewCommands:wpilibNewCommands-java:2024.3.2"

// PathPlannerLib
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/net/frc5183/librobot/math/curve/Curve.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package net.frc5183.librobot.math.curve;

/**
* An abstract class which represents a mathematical function/curve.
*/
public abstract class Curve {
/**
* Returns the value of the curve at the given x value.
* @param x The x value to evaluate the curve at.
* @return The value of the curve at the given x value.
*/
public abstract Double curve(Double x);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package net.frc5183.librobot.math.curve;

/**
* A {@link TimedCurve} that toggles between two curves.
*/
public class DoubleTimedCurve extends TimedCurve {
/**
* The curve to use when the curve is enabled.
*/
private Curve enabledCurve;

/**
* The curve to use when the curve is disabled.
*/
private Curve disabledCurve;

/**
* Creates a new {@link DoubleTimedCurve} with the given curves and delays.
* @param enabledCurve The curve to use when this curve is enabled.
* @param disabledCurve The curve to use when this curve is disabled.
* @param delayEnabled The delay in seconds before the curve is enabled.
* @param delayDisabled The delay in seconds before the curve is disabled.
* @see TimedCurve#TimedCurve(Double, Double)
*/
public DoubleTimedCurve(Curve enabledCurve, Curve disabledCurve, Double delayEnabled, Double delayDisabled) {
super(delayEnabled, delayDisabled);
this.enabledCurve = enabledCurve;
this.disabledCurve = disabledCurve;
}

@Override
protected Double enabled(Double x) {
return enabledCurve.curve(x);
}

@Override
protected Double disabled(Double x) {
return disabledCurve.curve(x);
}

/**
* Returns the curve to use when this curve is enabled.
* @return The curve to use when this curve is enabled.
*/
public Curve getEnabledCurve() {
return enabledCurve;
}

/**
* Sets the curve to use when this curve is enabled.
* @param enabledCurve The new curve to use when the curve is enabled.
*/
public void setEnabledCurve(Curve enabledCurve) {
this.enabledCurve = enabledCurve;
}

/**
* Returns the curve to use when this curve is disabled.
* @return The curve to use when this curve is disabled.
*/
public Curve getDisabledCurve() {
return disabledCurve;
}

/**
* Sets the curve to use when this curve is disabled.
* @param disabledCurve The new curve to use when the curve is disabled.
*/
public void setDisabledCurve(Curve disabledCurve) {
this.disabledCurve = disabledCurve;
}
Baconing marked this conversation as resolved.
Show resolved Hide resolved
}
Trip-kun marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package net.frc5183.librobot.math.curve;

/**
* An {@link Curve} which represents an exponential function in the form y = (x/|x|) * ((1 + k)^|x| - 1) / k.
* Where k is the exaggeration of the curve.
*/
public class ExponentialCurve extends Curve {
/**
* The exaggeration of the curve.
*/
private double exaggeration;

/**
* Creates a new {@link ExponentialCurve} with the given exaggeration.
* @param exaggeration The exaggeration of the curve.
*/
public ExponentialCurve(double exaggeration) {
this.exaggeration = exaggeration;
}
Baconing marked this conversation as resolved.
Show resolved Hide resolved

@Override
public Double curve(Double x) {
if (x == 0) return 0d;

Check warning on line 23 in src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java

View workflow job for this annotation

GitHub Actions / pmd

This statement should have braces

Enforce a policy for braces on control statements. It is recommended to use braces on 'if ... else' statements and loop statements, even if they are optional. This usually makes the code clearer, and helps prepare the future when you need to add another statement. That said, this rule lets you control which statements are required to have braces via properties. From 6.2.0 on, this rule supersedes WhileLoopMustUseBraces, ForLoopMustUseBraces, IfStmtMustUseBraces, and IfElseStmtMustUseBraces. ControlStatementBraces (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#controlstatementbraces

// y = (x/|x|) * ((1 + exaggeration)^|x| - 1) / exaggeration
return (x / Math.abs(x)) * ((Math.pow(1 + exaggeration, Math.abs(x))) - 1) / exaggeration;

Check warning on line 26 in src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java

View workflow job for this annotation

GitHub Actions / pmd

Useless parentheses.

Parenthesized expressions are used to override the default operator precedence rules. Parentheses whose removal would not change the relative nesting of operators are unnecessary, because they don't change the semantics of the enclosing expression. Some parentheses that strictly speaking are unnecessary, may still be considered useful for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses: - "Clarifying" parentheses, which separate operators of difference precedence. While unnecessary, they make precedence rules explicit, which may be useful for rarely used operators. For example: ```java (a + b) & c // is equivalent to `a + b & c`, but probably clearer ``` Unset the property `ignoreClarifying` to report them. - "Balancing" parentheses, which are unnecessary but visually balance out another pair of parentheses around an equality operator. For example, those two expressions are equivalent: ```java (a == null) != (b == null) a == null != (b == null) ``` The parentheses on the right are required, and the parentheses on the left are just more visually pleasing. Unset the property `ignoreBalancing` to report them. UselessParentheses (Priority: 4, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#uselessparentheses

Check warning on line 26 in src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java

View workflow job for this annotation

GitHub Actions / pmd

Useless parentheses.

Parenthesized expressions are used to override the default operator precedence rules. Parentheses whose removal would not change the relative nesting of operators are unnecessary, because they don't change the semantics of the enclosing expression. Some parentheses that strictly speaking are unnecessary, may still be considered useful for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses: - "Clarifying" parentheses, which separate operators of difference precedence. While unnecessary, they make precedence rules explicit, which may be useful for rarely used operators. For example: ```java (a + b) & c // is equivalent to `a + b & c`, but probably clearer ``` Unset the property `ignoreClarifying` to report them. - "Balancing" parentheses, which are unnecessary but visually balance out another pair of parentheses around an equality operator. For example, those two expressions are equivalent: ```java (a == null) != (b == null) a == null != (b == null) ``` The parentheses on the right are required, and the parentheses on the left are just more visually pleasing. Unset the property `ignoreBalancing` to report them. UselessParentheses (Priority: 4, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#uselessparentheses
}

/**
* Returns the exaggeration of the curve.
* @return The exaggeration of the curve.
*/
public double getExaggeration() {
return exaggeration;
}

/**
* Sets the exaggeration of the curve.
* @param exaggeration The new exaggeration of the curve.
*/
public void setExaggeration(double exaggeration) {
this.exaggeration = exaggeration;
}
Baconing marked this conversation as resolved.
Show resolved Hide resolved
}
87 changes: 87 additions & 0 deletions src/main/java/net/frc5183/librobot/math/curve/LimitedCurve.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package net.frc5183.librobot.math.curve;

/**
* A {@link Curve} which limits the output of another curve to a certain range.
*/
public class LimitedCurve extends Curve {
/**
* The curve to limit.
*/
private Curve curve;

/**
* The minimum value of the curve.
*/
private Double min;

/**
* The maximum value of the curve.
*/
private Double max;
Baconing marked this conversation as resolved.
Show resolved Hide resolved

/**
* Creates a new {@link LimitedCurve} with the given curve, minimum, and maximum values.
* @param curve The curve to limit.
* @param min The minimum value of the curve.
* @param max The maximum value of the curve.
*/
public LimitedCurve(Curve curve, Double min, Double max) {
this.curve = curve;
this.min = min;
this.max = max;
}
Baconing marked this conversation as resolved.
Show resolved Hide resolved

@Override
public Double curve(Double x) {
Double y = curve.curve(x);
return Math.min(Math.max(y, min), max);
}
Baconing marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns the curve to limit.
* @return The curve to limit.
*/
public Curve getCurve() {
return curve;
}

/**
* Sets the curve to limit.
* @param curve The new curve to limit.
*/
public void setCurve(Curve curve) {
this.curve = curve;
}

/**
* Returns the minimum value of the curve.
* @return The minimum value of the curve.
*/
public Double getMin() {
return min;
}

/**
* Sets the minimum value of the curve.
* @param min The new minimum value of the curve.
*/
public void setMin(Double min) {
this.min = min;
}

/**
* Returns the maximum value of the curve.
* @return The maximum value of the curve.
*/
public Double getMax() {
return max;
}

/**
* Sets the maximum value of the curve.
* @param max The new maximum value of the curve.
*/
public void setMax(Double max) {
this.max = max;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation in setter methods.

The getter and setter methods are well-implemented and documented. However, the setter methods lack input validation, which could lead to inconsistent state.

Consider adding validation in the setter methods:

 public void setCurve(Curve curve) {
+    if (curve == null) {
+        throw new IllegalArgumentException("Curve cannot be null");
+    }
     this.curve = curve;
 }

 public void setMin(Double min) {
+    if (max != null && min != null && min > max) {
+        throw new IllegalArgumentException("Min value cannot be greater than max value");
+    }
     this.min = min;
 }

 public void setMax(Double max) {
+    if (min != null && max != null && max < min) {
+        throw new IllegalArgumentException("Max value cannot be less than min value");
+    }
     this.max = max;
 }

These changes ensure that the curve is not set to null and that the min and max values remain consistent when changed individually.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Returns the curve to limit.
* @return The curve to limit.
*/
public Curve getCurve() {
return curve;
}
/**
* Sets the curve to limit.
* @param curve The new curve to limit.
*/
public void setCurve(Curve curve) {
this.curve = curve;
}
/**
* Returns the minimum value of the curve.
* @return The minimum value of the curve.
*/
public Double getMin() {
return min;
}
/**
* Sets the minimum value of the curve.
* @param min The new minimum value of the curve.
*/
public void setMin(Double min) {
this.min = min;
}
/**
* Returns the maximum value of the curve.
* @return The maximum value of the curve.
*/
public Double getMax() {
return max;
}
/**
* Sets the maximum value of the curve.
* @param max The new maximum value of the curve.
*/
public void setMax(Double max) {
this.max = max;
}
/**
* Returns the curve to limit.
* @return The curve to limit.
*/
public Curve getCurve() {
return curve;
}
/**
* Sets the curve to limit.
* @param curve The new curve to limit.
*/
public void setCurve(Curve curve) {
if (curve == null) {
throw new IllegalArgumentException("Curve cannot be null");
}
this.curve = curve;
}
/**
* Returns the minimum value of the curve.
* @return The minimum value of the curve.
*/
public Double getMin() {
return min;
}
/**
* Sets the minimum value of the curve.
* @param min The new minimum value of the curve.
*/
public void setMin(Double min) {
if (max != null && min != null && min > max) {
throw new IllegalArgumentException("Min value cannot be greater than max value");
}
this.min = min;
}
/**
* Returns the maximum value of the curve.
* @return The maximum value of the curve.
*/
public Double getMax() {
return max;
}
/**
* Sets the maximum value of the curve.
* @param max The new maximum value of the curve.
*/
public void setMax(Double max) {
if (min != null && max != null && max < min) {
throw new IllegalArgumentException("Max value cannot be less than min value");
}
this.max = max;
}

}
64 changes: 64 additions & 0 deletions src/main/java/net/frc5183/librobot/math/curve/LinearCurve.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package net.frc5183.librobot.math.curve;

/**
* A {@link Curve} which represents a linear equation in the form y = mx + b.
*/
public class LinearCurve extends Curve {
/**
* The slope of the curve.
*/
private double slope;

/**
* The y-intercept of the curve.
*/
private double yIntercept;

/**
* Creates a new {@link LinearCurve} with the given slope and y-intercept.
* @param slope The slope of the curve.
* @param yIntercept The y-intercept of the curve.
*/
public LinearCurve(double slope, double yIntercept) {
this.slope = slope;
this.yIntercept = yIntercept;
}

@Override
public Double curve(Double x) {
// y = mx + b
return slope * x + yIntercept;
}
Baconing marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns the slope of the curve.
* @return The slope of the curve.
*/
public double getSlope() {
return slope;
}

/**
* Sets the slope of the curve.
* @param slope The new slope of the curve.
*/
public void setSlope(double slope) {
this.slope = slope;
}

/**
* Returns the y-intercept of the curve.
* @return The y-intercept of the curve.
*/
public double getYIntercept() {
return yIntercept;
}

/**
* Sets the y-intercept of the curve.
* @param yIntercept The new y-intercept of the curve.
*/
public void setYIntercept(double yIntercept) {
this.yIntercept = yIntercept;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package net.frc5183.librobot.math.curve;

/**
* A @{link TimedCurve} that toggles between x (when disabled) and curve(x) (when enabled).
*/
public class NormalTimedCurve extends TimedCurve {
/**
* The curve to use when this curve is enabled.
*/
private Curve curve;

/**
* Creates a new {@link NormalTimedCurve} with the given curve and delays.
* @param curve The curve to use when this curve is enabled.
* @param delayEnabled The delay in seconds before the curve is enabled.
* @param delayDisabled The delay in seconds before the curve is disabled.
*/
public NormalTimedCurve(Curve curve, Double delayEnabled, Double delayDisabled) {
super(delayEnabled, delayDisabled);
this.curve = curve;
}

@Override
protected Double enabled(Double x) {
return curve(x);
}

@Override
protected Double disabled(Double x) {
return x;
}

/**
* Returns the curve to use when this curve is enabled.
* @return The curve to use when this curve is enabled.
*/
public Curve getCurve() {
return curve;
}

/**
* Sets the curve to use when this curve is enabled.
* @param curve The new curve to use when the curve is enabled.
*/
public void setCurve(Curve curve) {
this.curve = curve;
}
}
Loading
Loading