Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
cf19115
choreolib: add trajectory mirroring for x and y
RohanBhattacharyya Mar 23, 2026
aabae6b
Fix mirrorX() and getActiveYearInfo()
RohanBhattacharyya Mar 23, 2026
a743b93
Refactor alliance flipping
shueja Mar 23, 2026
81541ac
Merge branch 'main' of github.com:RohanBhattacharyya/Choreo into pr/R…
shueja Mar 23, 2026
0790099
Updated SampleFlippingTest.java for new ChoreoAllianceFlipUtil
RohanBhattacharyya Mar 23, 2026
ad43cf2
Add Javadoc (Copilot used for doc comments, but not for code)
shueja Mar 23, 2026
10266b5
Merge branch 'main' of github.com:RohanBhattacharyya/Choreo into pr/R…
shueja Mar 23, 2026
fff2c6e
Added broken tests. Tests require sample.equals consider mod2pi headi…
shueja Mar 23, 2026
ed5bd2d
Add mirrorX() and mirrorY() instance methods to AutoTrajectory
RohanBhattacharyya Mar 23, 2026
a4f53ba
Fix javadoc comments
RohanBhattacharyya Mar 23, 2026
03658f5
Merge branch 'main' into main
RohanBhattacharyya Mar 23, 2026
f273ab2
Add mirror inverse test and proper radian equality
RohanBhattacharyya Mar 23, 2026
d8f6982
Add DifferentialSample mirror inverse and proper heading equality
RohanBhattacharyya Mar 23, 2026
7607ec0
Port to C++ and Python with copilot
RohanBhattacharyya Mar 23, 2026
0026bf8
Fix and finish port including alliance flipping refactor from Java
RohanBhattacharyya Mar 23, 2026
865b6f9
Remove <cmath>
RohanBhattacharyya Mar 23, 2026
4f304de
Revert choreolib to d8f6982
RohanBhattacharyya Mar 23, 2026
cf52a9b
Fix spotless violations
RohanBhattacharyya Mar 23, 2026
23825fb
Revert heading comparison to raw isNear
RohanBhattacharyya Mar 23, 2026
8931b98
Avoid having extra abstract Flipper classes
shueja Mar 24, 2026
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
2 changes: 2 additions & 0 deletions choreolib/src/main/java/choreo/auto/AutoChooser.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public AutoChooser(String doNothingName) {
}

/**
* Returns the name of the default do-nothing option.
*
* @return the name of the default do-nothing option.
*/
public String getDefaultName() {
Expand Down
44 changes: 44 additions & 0 deletions choreolib/src/main/java/choreo/auto/AutoTrajectory.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public class AutoTrajectory {
private final Timer inactiveTimer = new Timer();
private final Subsystem driveSubsystem;
private final AutoRoutine routine;
private final AutoBindings bindings;

/**
* A way to create slightly less triggers for many actions. Not static as to not leak triggers
Expand Down Expand Up @@ -115,6 +116,7 @@ <SampleType extends TrajectorySample<SampleType>> AutoTrajectory(
this.routine = routine;
this.offTrigger = new Trigger(routine.loop(), () -> false);
this.trajectoryLogger = trajectoryLogger;
this.bindings = bindings;

bindings.getBindings().forEach((key, value) -> active().and(atTime(key)).onTrue(value));
}
Expand Down Expand Up @@ -268,6 +270,48 @@ Trajectory<SampleType> getRawTrajectory() {
return (Trajectory<SampleType>) trajectory;
}

/**
* Returns this auto trajectory, mirrored across the field width.
*
* @param <SampleType> The type of the trajectory samples.
* @return this auto trajectory, mirrored across the field width.
*/
@SuppressWarnings("unchecked")
public <SampleType extends TrajectorySample<SampleType>> AutoTrajectory mirrorX() {
return new AutoTrajectory(
name,
(Trajectory<SampleType>) trajectory.mirrorX(),
poseSupplier,
resetOdometry,
(Consumer<SampleType>) controller,
allianceCtx,
(TrajectoryLogger<SampleType>) trajectoryLogger,
driveSubsystem,
routine,
bindings);
}

/**
* Returns this auto trajectory, mirrored across the field length.
*
* @param <SampleType> The type of the trajectory samples.
* @return this auto trajectory, mirrored across the field length.
*/
@SuppressWarnings("unchecked")
public <SampleType extends TrajectorySample<SampleType>> AutoTrajectory mirrorY() {
return new AutoTrajectory(
name,
(Trajectory<SampleType>) trajectory.mirrorY(),
poseSupplier,
resetOdometry,
(Consumer<SampleType>) controller,
allianceCtx,
(TrajectoryLogger<SampleType>) trajectoryLogger,
driveSubsystem,
routine,
bindings);
}

/**
* Will get the starting pose of the trajectory.
*
Expand Down
65 changes: 23 additions & 42 deletions choreolib/src/main/java/choreo/trajectory/DifferentialSample.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,36 +171,17 @@ public DifferentialSample interpolate(DifferentialSample endValue, double timest
}

public DifferentialSample flipped() {
return switch (ChoreoAllianceFlipUtil.getFlipper()) {
case MIRRORED ->
new DifferentialSample(
t,
ChoreoAllianceFlipUtil.flipX(x),
ChoreoAllianceFlipUtil.flipY(y), // No-op for mirroring
ChoreoAllianceFlipUtil.flipHeading(heading),
vr,
vl,
-omega,
ar,
al,
-alpha,
fr,
fl);
case ROTATE_AROUND ->
new DifferentialSample(
t,
ChoreoAllianceFlipUtil.flipX(x),
ChoreoAllianceFlipUtil.flipY(y),
ChoreoAllianceFlipUtil.flipHeading(heading),
vl,
vr,
omega,
al,
ar,
alpha,
fl,
fr);
};
return ChoreoAllianceFlipUtil.getFlipper().flip(this);
}

@Override
Copy link
Collaborator

Choose a reason for hiding this comment

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

Added new public methods:

  • DifferentialSample::mirrorY and DifferentialSample::mirrorX
  • SwerveSample::mirrorY and SwerveSample::mirrorX

public DifferentialSample mirrorY() {
return ChoreoAllianceFlipUtil.getMirrorY().flip(this);
}

@Override
public DifferentialSample mirrorX() {
return ChoreoAllianceFlipUtil.getMirrorX().flip(this);
}

public DifferentialSample offsetBy(double timestampOffset) {
Expand Down Expand Up @@ -287,17 +268,17 @@ public boolean equals(Object obj) {
}

var other = (DifferentialSample) obj;
return this.t == other.t
&& this.x == other.x
&& this.y == other.y
&& this.heading == other.heading
&& this.vl == other.vl
&& this.vr == other.vr
&& this.omega == other.omega
&& this.al == other.al
&& this.ar == other.ar
&& this.alpha == other.alpha
&& this.fl == other.fl
&& this.fr == other.fr;
return MathUtil.isNear(this.t, other.t, 1E-6)
&& MathUtil.isNear(this.x, other.x, 1E-6)
&& MathUtil.isNear(this.y, other.y, 1E-6)
&& MathUtil.isNear(this.heading, other.heading, 1E-6)
&& MathUtil.isNear(this.vl, other.vl, 1E-6)
&& MathUtil.isNear(this.vr, other.vr, 1E-6)
&& MathUtil.isNear(this.omega, other.omega, 1E-6)
&& MathUtil.isNear(this.al, other.al, 1E-6)
&& MathUtil.isNear(this.ar, other.ar, 1E-6)
&& MathUtil.isNear(this.alpha, other.alpha, 1E-6)
&& MathUtil.isNear(this.fl, other.fl, 1E-6)
&& MathUtil.isNear(this.fr, other.fr, 1E-6);
}
}
82 changes: 23 additions & 59 deletions choreolib/src/main/java/choreo/trajectory/SwerveSample.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.util.struct.Struct;
import java.nio.ByteBuffer;
import java.util.Arrays;

/** A single swerve robot sample in a Trajectory. */
public class SwerveSample implements TrajectorySample<SwerveSample> {
Expand Down Expand Up @@ -199,52 +198,17 @@ public SwerveSample offsetBy(double timestampOffset) {

@Override
public SwerveSample flipped() {
return switch (ChoreoAllianceFlipUtil.getFlipper()) {
case MIRRORED ->
new SwerveSample(
this.t,
ChoreoAllianceFlipUtil.flipX(this.x),
ChoreoAllianceFlipUtil.flipY(this.y),
ChoreoAllianceFlipUtil.flipHeading(this.heading),
-this.vx,
this.vy,
-this.omega,
-this.ax,
this.ay,
-this.alpha,
// FL, FR, BL, BR
// Mirrored
// -FR, -FL, -BR, -BL
new double[] {
-this.moduleForcesX()[1],
-this.moduleForcesX()[0],
-this.moduleForcesX()[3],
-this.moduleForcesX()[2]
},
// FL, FR, BL, BR
// Mirrored
// FR, FL, BR, BL
new double[] {
this.moduleForcesY()[1],
this.moduleForcesY()[0],
this.moduleForcesY()[3],
this.moduleForcesY()[2]
});
case ROTATE_AROUND ->
new SwerveSample(
this.t,
ChoreoAllianceFlipUtil.flipX(this.x),
ChoreoAllianceFlipUtil.flipY(this.y),
ChoreoAllianceFlipUtil.flipHeading(this.heading),
-this.vx,
-this.vy,
this.omega,
-this.ax,
-this.ay,
this.alpha,
Arrays.stream(this.moduleForcesX()).map(x -> -x).toArray(),
Arrays.stream(this.moduleForcesY()).map(y -> -y).toArray());
};
return ChoreoAllianceFlipUtil.flip(this);
}

@Override
public SwerveSample mirrorY() {
return ChoreoAllianceFlipUtil.getMirrorY().flip(this);
}

@Override
public SwerveSample mirrorX() {
return ChoreoAllianceFlipUtil.getMirrorX().flip(this);
}

/** The struct for the SwerveSample class. */
Expand Down Expand Up @@ -330,19 +294,19 @@ public boolean equals(Object obj) {
}

var other = (SwerveSample) obj;
return this.t == other.t
&& this.x == other.x
&& this.y == other.y
&& this.heading == other.heading
&& this.vx == other.vx
&& this.vy == other.vy
&& this.omega == other.omega
&& this.ax == other.ax
&& this.ay == other.ay
&& this.alpha == other.alpha
return MathUtil.isNear(this.t, other.t, 1E-6)
&& MathUtil.isNear(this.x, other.x, 1E-6)
&& MathUtil.isNear(this.y, other.y, 1E-6)
&& MathUtil.isNear(this.heading, other.heading, 1E-6)
&& MathUtil.isNear(this.vx, other.vx, 1E-6)
&& MathUtil.isNear(this.vy, other.vy, 1E-6)
&& MathUtil.isNear(this.omega, other.omega, 1E-6)
&& MathUtil.isNear(this.ax, other.ax, 1E-6)
&& MathUtil.isNear(this.ay, other.ay, 1E-6)
&& MathUtil.isNear(this.alpha, other.alpha, 1E-6)
&& ChoreoArrayUtil.zipEquals(
this.fx, other.fx, (a, b) -> a.doubleValue() == b.doubleValue())
this.fx, other.fx, (a, b) -> MathUtil.isNear(a.doubleValue(), b.doubleValue(), 1E-6))
&& ChoreoArrayUtil.zipEquals(
this.fy, other.fy, (a, b) -> a.doubleValue() == b.doubleValue());
this.fy, other.fy, (a, b) -> MathUtil.isNear(a.doubleValue(), b.doubleValue(), 1E-6));
}
}
26 changes: 26 additions & 0 deletions choreolib/src/main/java/choreo/trajectory/Trajectory.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,32 @@ public Trajectory<SampleType> flipped() {
return new Trajectory<SampleType>(this.name, flippedStates, this.splits, this.events);
}

/**
* Returns this trajectory, mirrored across the field width.
*
* @return this trajectory, mirrored across the field width.
*/
public Trajectory<SampleType> mirrorX() {
var flippedStates = new ArrayList<SampleType>();
for (var state : samples) {
flippedStates.add(state.mirrorX());
}
return new Trajectory<SampleType>(this.name, flippedStates, this.splits, this.events);
}

/**
* Returns this trajectory, mirrored across the field length.
*
* @return this trajectory, mirrored across the field length.
*/
public Trajectory<SampleType> mirrorY() {
var flippedStates = new ArrayList<SampleType>();
for (var state : samples) {
flippedStates.add(state.mirrorY());
}
return new Trajectory<SampleType>(this.name, flippedStates, this.splits, this.events);
}

/**
* Returns a list of all events with the given name in the trajectory.
*
Expand Down
14 changes: 14 additions & 0 deletions choreolib/src/main/java/choreo/trajectory/TrajectorySample.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ public interface TrajectorySample<Self extends TrajectorySample<Self>>
*/
Self flipped();

/**
* Returns this sample, mirrored across the field length.
*
* @return this sample, mirrored across the field length.
*/
Self mirrorY();

/**
* Returns this sample, mirrored across the field width.
*
* @return this sample, mirrored across the field width.
*/
Self mirrorX();

/**
* Returns this sample, offset by the given timestamp.
*
Expand Down
Loading