[wpiunits] Java units API rewrite (#6958)

Java generics are too limited to do what we need. This refactors generic code previously in Unit and Measure into unit-specific classes that can have unit-safe math operations (notably, times and divide) that can return values in known units instead of a wildcarded Measure<?>.

Unit-specific measure implementations are automatically generated by ./wpiunits/generate_units.py, which generates generic interfaces and mutable and immutable implementations of those interfaces. These make up the bulk of the diff of this PR (approximately 9300 LOC).

This also adds units for angular and linear velocities, accelerations, and momenta; moment of inertia; and torque.
This commit is contained in:
Sam Carlberg
2024-09-07 13:59:29 -04:00
committed by GitHub
parent 496e7c1bba
commit a9b885070e
178 changed files with 14750 additions and 2158 deletions

View File

@@ -4,111 +4,54 @@
package edu.wpi.first.units;
import java.util.Objects;
import edu.wpi.first.units.measure.Dimensionless;
/**
* A specialization of {@link Measure} that allows for mutability. This is intended to be used for
* memory use reasons (such as on the memory-restricted roboRIO 1 or 2 or SBC coprocessors) and
* should NOT be exposed in the public API for a class that uses it.
* A mutable measurement can be used to keep a single object allocation and reference whose state is
* mutated or changed as it is used. This is helpful for optimizing memory usage to keep garbage
* collection time - and its associated loop overruns - to a minimum.
*
* <p>The advantage of using this class is to reuse one instance of a measurement object, as opposed
* to instantiating a new immutable instance every time an operation is performed. This will reduce
* memory pressure, but comes at the cost of increased code complexity and sensitivity to race
* conditions if misused.
*
* <p>Any unsafe methods are prefixed with {@code mut_*}, such as {@link #mut_plus(Measure)} or
* {@link #mut_replace(Measure)}. These methods will change the internal state of the measurement
* object, and as such can be dangerous to use. They are primarily intended for use to track
* internal state of things like sensors
*
* @param <U> the type of the unit of measure
* @param <U> The dimension of measurement.
* @param <Base> The base measure type.
* @param <MutSelf> The self type. This MUST inherit from the base measure type.
*/
public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
private double m_magnitude;
private double m_baseUnitMagnitude;
private U m_unit;
private MutableMeasure(double initialMagnitude, double baseUnitMagnitude, U unit) {
m_magnitude = initialMagnitude;
m_baseUnitMagnitude = baseUnitMagnitude;
m_unit = unit;
}
public interface MutableMeasure<
U extends Unit, Base extends Measure<U>, MutSelf extends MutableMeasure<U, Base, MutSelf>>
extends Measure<U> {
/**
* Overwrites the state of this measure with new values.
*
* @param magnitude the new magnitude in terms of the new unit
* @param newUnit the new unit
* @return this measure
*/
MutSelf mut_replace(double magnitude, U newUnit);
/**
* Creates a new mutable measure that is a copy of the given one.
* Overwrites the state of this measure and replaces it completely with values from the given one.
* The magnitude, base unit magnitude, and unit will all be copied. This is functionally the same
* as calling {@code other.mutableCopy()}, but copying to a pre-existing mutable measure instead
* of instantiating a new one.
*
* @param <U> the type of the units of measure
* @param measure the measure to create a mutable copy of
* @return a new mutable measure with an initial state equal to the given measure
* @param other the other measure to copy values from
* @return this measure
*/
public static <U extends Unit<U>> MutableMeasure<U> mutable(Measure<U> measure) {
return new MutableMeasure<>(measure.magnitude(), measure.baseUnitMagnitude(), measure.unit());
}
/**
* Creates a new mutable measure with a magnitude of 0 in the given unit.
*
* @param <U> the type of the units of measure
* @param unit the unit of measure
* @return a new mutable measure
*/
public static <U extends Unit<U>> MutableMeasure<U> zero(U unit) {
return mutable(unit.zero());
}
/**
* Creates a new mutable measure in the given unit with a magnitude equal to the given one in base
* units.
*
* @param <U> the type of the units of measure
* @param baseUnitMagnitude the magnitude of the measure, in terms of the base unit of measure
* @param unit the unit of measure
* @return a new mutable measure
*/
public static <U extends Unit<U>> MutableMeasure<U> ofBaseUnits(
double baseUnitMagnitude, U unit) {
return new MutableMeasure<>(unit.fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, unit);
}
/**
* Creates a new mutable measure in the given unit with a magnitude in terms of that unit.
*
* @param <U> the type of the units of measure
* @param relativeMagnitude the magnitude of the measure
* @param unit the unit of measure
* @return a new mutable measure
*/
public static <U extends Unit<U>> MutableMeasure<U> ofRelativeUnits(
double relativeMagnitude, U unit) {
return new MutableMeasure<>(relativeMagnitude, unit.toBaseUnits(relativeMagnitude), unit);
default MutSelf mut_replace(Base other) {
return mut_replace(other.magnitude(), other.unit());
}
@Override
public double magnitude() {
return m_magnitude;
}
@Override
public double baseUnitMagnitude() {
return m_baseUnitMagnitude;
}
@Override
public U unit() {
return m_unit;
}
// UNSAFE
Base copy();
/**
* Sets the new magnitude of the measurement. The magnitude must be in terms of the {@link
* #unit()}.
*
* @param magnitude the new magnitude of the measurement
* @return this mutable measure
*/
public void mut_setMagnitude(double magnitude) {
m_magnitude = magnitude;
m_baseUnitMagnitude = m_unit.toBaseUnits(magnitude);
default MutSelf mut_setMagnitude(double magnitude) {
return mut_replace(magnitude, unit());
}
/**
@@ -116,37 +59,10 @@ public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
* the current unit.
*
* @param baseUnitMagnitude the new magnitude of the measurement
* @return this mutable measure
*/
public void mut_setBaseUnitMagnitude(double baseUnitMagnitude) {
m_baseUnitMagnitude = baseUnitMagnitude;
m_magnitude = m_unit.fromBaseUnits(baseUnitMagnitude);
}
/**
* Overwrites the state of this measure and replaces it with values from the given one.
*
* @param other the other measure to copy values from
* @return this measure
*/
public MutableMeasure<U> mut_replace(Measure<U> other) {
m_magnitude = other.magnitude();
m_baseUnitMagnitude = other.baseUnitMagnitude();
m_unit = other.unit();
return this;
}
/**
* Overwrites the state of this measure with new values.
*
* @param magnitude the new magnitude in terms of the new unit
* @param unit the new unit
* @return this measure
*/
public MutableMeasure<U> mut_replace(double magnitude, U unit) {
this.m_magnitude = magnitude;
this.m_baseUnitMagnitude = unit.toBaseUnits(magnitude);
this.m_unit = unit;
return this;
default MutSelf mut_setBaseUnitMagnitude(double baseUnitMagnitude) {
return mut_replace(unit().fromBaseUnits(baseUnitMagnitude), unit());
}
/**
@@ -156,29 +72,20 @@ public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
* @param raw the raw value to accumulate by
* @return the measure
*/
public MutableMeasure<U> mut_acc(double raw) {
this.m_magnitude += raw;
this.m_baseUnitMagnitude += m_unit.toBaseUnits(raw);
return this;
default MutSelf mut_acc(double raw) {
return mut_setBaseUnitMagnitude(magnitude() + raw);
}
/**
* Increments the current magnitude of the measure by the amount of the given measure.
*
* @param other the measure whose value should be added to this one
* @return the measure
* @return this measure
*/
public MutableMeasure<U> mut_acc(Measure<U> other) {
m_baseUnitMagnitude += other.baseUnitMagnitude();
// can't naively use m_magnitude += other.in(m_unit) because the units may not
// be scalar multiples (eg adding 0C to 100K should result in 373.15K, not 100K)
m_magnitude = m_unit.fromBaseUnits(m_baseUnitMagnitude);
return this;
default MutSelf mut_acc(Base other) {
return mut_setMagnitude(magnitude() + unit().fromBaseUnits(other.baseUnitMagnitude()));
}
// Math
/**
* Adds another measurement to this one. This will mutate the object instead of generating a new
* measurement object.
@@ -186,8 +93,8 @@ public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
* @param other the measurement to add
* @return this measure
*/
public MutableMeasure<U> mut_plus(Measure<U> other) {
return mut_plus(other.magnitude(), other.unit());
default MutSelf mut_plus(Base other) {
return mut_acc(other);
}
/**
@@ -196,36 +103,35 @@ public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
* having to wrap raw numbers in a {@code Measure} object and pay for an object allocation.
*
* @param magnitude the magnitude of the other measurement.
* @param unit the unit of the other measurement
* @param otherUnit the unit of the other measurement
* @return this measure
*/
public MutableMeasure<U> mut_plus(double magnitude, U unit) {
mut_setBaseUnitMagnitude(m_baseUnitMagnitude + unit.toBaseUnits(magnitude));
return this;
default MutSelf mut_plus(double magnitude, U otherUnit) {
return mut_setBaseUnitMagnitude(magnitude() + otherUnit.toBaseUnits(magnitude));
}
/**
* Subtracts another measurement to this one. This will mutate the object instead of generating a
* new measurement object.
*
* @param other the measurement to add
* @param other the measurement to subtract from this one
* @return this measure
*/
public MutableMeasure<U> mut_minus(Measure<U> other) {
return mut_minus(other.magnitude(), other.unit());
default MutSelf mut_minus(Base other) {
return mut_setBaseUnitMagnitude(baseUnitMagnitude() - other.baseUnitMagnitude());
}
/**
* Subtracts another measurement to this one. This will mutate the object instead of generating a
* new measurement object. This is a denormalized version of {@link #mut_minus(Measure)} to avoid
* having to wrap raw numbers in a {@code Measure} object and pay for an object allocation.
* Subtracts another measurement from this one. This will mutate the object instead of generating
* a new measurement object. This is a denormalized version of {@link #mut_minus(Measure)} to
* avoid having to wrap raw numbers in a {@code Measure} object and pay for an object allocation.
*
* @param magnitude the magnitude of the other measurement.
* @param unit the unit of the other measurement
* @param otherUnit the unit of the other measurement
* @return this measure
*/
public MutableMeasure<U> mut_minus(double magnitude, U unit) {
return mut_plus(-magnitude, unit);
default MutSelf mut_minus(double magnitude, U otherUnit) {
return mut_setBaseUnitMagnitude(baseUnitMagnitude() - otherUnit.toBaseUnits(magnitude));
}
/**
@@ -235,9 +141,8 @@ public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
* @param multiplier the multiplier to scale the measurement by
* @return this measure
*/
public MutableMeasure<U> mut_times(double multiplier) {
mut_setBaseUnitMagnitude(m_baseUnitMagnitude * multiplier);
return this;
default MutSelf mut_times(double multiplier) {
return mut_setBaseUnitMagnitude(baseUnitMagnitude() * multiplier);
}
/**
@@ -247,7 +152,7 @@ public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
* @param multiplier the multiplier to scale the measurement by
* @return this measure
*/
public MutableMeasure<U> mut_times(Measure<? extends Dimensionless> multiplier) {
default MutSelf mut_times(Dimensionless multiplier) {
return mut_times(multiplier.baseUnitMagnitude());
}
@@ -258,9 +163,8 @@ public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
* @param divisor the divisor to scale the measurement by
* @return this measure
*/
public MutableMeasure<U> mut_divide(double divisor) {
mut_setBaseUnitMagnitude(m_baseUnitMagnitude / divisor);
return this;
default MutSelf mut_divide(double divisor) {
return mut_times(1 / divisor);
}
/**
@@ -270,29 +174,7 @@ public final class MutableMeasure<U extends Unit<U>> implements Measure<U> {
* @param divisor the divisor to scale the measurement by
* @return this measure
*/
public MutableMeasure<U> mut_divide(Measure<? extends Dimensionless> divisor) {
default MutSelf mut_divide(Dimensionless divisor) {
return mut_divide(divisor.baseUnitMagnitude());
}
@Override
public Measure<U> copy() {
return new ImmutableMeasure<>(m_magnitude, m_baseUnitMagnitude, m_unit);
}
@Override
public String toString() {
return toShortString();
}
@Override
public boolean equals(Object o) {
return o instanceof Measure<?> that
&& Objects.equals(m_unit, that.unit())
&& this.isEquivalent(that);
}
@Override
public int hashCode() {
return Objects.hash(m_magnitude, m_unit);
}
}