mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-28 02:11:43 +00:00
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.
288 lines
9.2 KiB
Java
288 lines
9.2 KiB
Java
// Copyright (c) FIRST and other WPILib contributors.
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
package edu.wpi.first.units;
|
|
|
|
import edu.wpi.first.units.measure.ImmutablePer;
|
|
import edu.wpi.first.units.measure.MutPer;
|
|
import edu.wpi.first.units.measure.Per;
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* Generic combinatory unit type that represents the proportion of one unit to another, such as
|
|
* Meters per Second or Radians per Celsius.
|
|
*
|
|
* @param <N> the type of the numerator unit
|
|
* @param <D> the type of the denominator unit
|
|
*/
|
|
public class PerUnit<N extends Unit, D extends Unit> extends Unit {
|
|
private final N m_numerator;
|
|
private final D m_denominator;
|
|
|
|
/**
|
|
* Keep a cache of created instances so expressions like Volts.per(Meter) don't do any allocations
|
|
* after the first.
|
|
*/
|
|
@SuppressWarnings("rawtypes")
|
|
private static final CombinatoryUnitCache<Unit, Unit, PerUnit> cache =
|
|
new CombinatoryUnitCache<>(PerUnit::new);
|
|
|
|
/**
|
|
* Creates a new proportional unit derived from the ratio of one unit to another. Consider using
|
|
* {@link #combine} instead of manually calling this constructor.
|
|
*
|
|
* @param numerator the numerator unit
|
|
* @param denominator the denominator unit
|
|
*/
|
|
private PerUnit(N numerator, D denominator) {
|
|
super(
|
|
numerator.isBaseUnit() && denominator.isBaseUnit()
|
|
? null
|
|
: combine(numerator.getBaseUnit(), denominator.getBaseUnit()),
|
|
numerator.toBaseUnits(1) / denominator.toBaseUnits(1),
|
|
numerator.name() + " per " + denominator.name(),
|
|
numerator.symbol() + "/" + denominator.symbol());
|
|
m_numerator = numerator;
|
|
m_denominator = denominator;
|
|
}
|
|
|
|
/**
|
|
* Creates a new proportional unit derived from the ratio of one unit to another. Subclasses of
|
|
* {@code PerUnit} should use this constructor.
|
|
*
|
|
* @param baseUnit the base unit. Set this to null if the unit being constructed is its own base
|
|
* unit
|
|
* @param numerator the numerator unit
|
|
* @param denominator the denominator unit
|
|
*/
|
|
protected PerUnit(PerUnit<N, D> baseUnit, N numerator, D denominator) {
|
|
super(
|
|
baseUnit,
|
|
numerator.toBaseUnits(1) / denominator.toBaseUnits(1),
|
|
numerator.name() + " per " + denominator.name(),
|
|
numerator.symbol() + "/" + denominator.symbol());
|
|
m_numerator = numerator;
|
|
m_denominator = denominator;
|
|
}
|
|
|
|
/** {@inheritDoc} */
|
|
PerUnit(
|
|
PerUnit<N, D> baseUnit,
|
|
UnaryFunction toBaseConverter,
|
|
UnaryFunction fromBaseConverter,
|
|
String name,
|
|
String symbol) {
|
|
super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol);
|
|
m_numerator = getBaseUnit().numerator();
|
|
m_denominator = getBaseUnit().denominator();
|
|
}
|
|
|
|
/**
|
|
* Creates a new PerUnit unit derived from an arbitrary numerator and time denominator units.
|
|
*
|
|
* <pre>
|
|
* PerUnit.combine(Volts, Meters) // possible PID constant
|
|
* </pre>
|
|
*
|
|
* @param <N> the type of the numerator unit
|
|
* @param <D> the type of the denominator unit
|
|
* @param numerator the numerator unit
|
|
* @param denominator the denominator for unit time
|
|
* @return the combined unit
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public static <N extends Unit, D extends Unit> PerUnit<N, D> combine(N numerator, D denominator) {
|
|
return cache.combine(numerator, denominator);
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("unchecked")
|
|
public PerUnit<N, D> getBaseUnit() {
|
|
return (PerUnit<N, D>) super.getBaseUnit();
|
|
}
|
|
|
|
/**
|
|
* Gets the numerator unit. For a {@code PerUnit<A, B>}, this will return the {@code A} unit.
|
|
*
|
|
* @return the numerator unit
|
|
*/
|
|
public N numerator() {
|
|
return m_numerator;
|
|
}
|
|
|
|
/**
|
|
* Gets the denominator unit. For a {@code PerUnit<A, B>}, this will return the {@code B} unit.
|
|
*
|
|
* @return the denominator unit
|
|
*/
|
|
public D denominator() {
|
|
return m_denominator;
|
|
}
|
|
|
|
/**
|
|
* Returns the reciprocal of this PerUnit.
|
|
*
|
|
* @return the reciprocal
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public PerUnit<D, N> reciprocal() {
|
|
if (m_numerator instanceof TimeUnit t) {
|
|
// Dividing by time, return a velocity
|
|
return (PerUnit<D, N>) VelocityUnit.combine(m_denominator, t);
|
|
} else {
|
|
// Generic case
|
|
return combine(m_denominator, m_numerator);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Multiplies this unit by a unit of its denominator.
|
|
*
|
|
* @param denom the denominator-typed unit to multiply by
|
|
* @return the result
|
|
*/
|
|
public N mult(D denom) {
|
|
if (denom.equivalent(denominator())) {
|
|
return numerator();
|
|
}
|
|
|
|
return Units.derive(numerator())
|
|
.toBase(denom.getConverterToBase().div(denominator().getConverterToBase()))
|
|
.fromBase(denom.getConverterFromBase().div(denominator().getConverterFromBase()))
|
|
.named(name() + " " + denom.name())
|
|
.symbol(symbol() + "-" + denom.symbol())
|
|
.make();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* <p>Note: When called on an object of type {@code PerUnit} (and <i>not</i> a subclass!), this
|
|
* method will always return a {@link edu.wpi.first.units.measure.Per} instance. If you want to
|
|
* avoid casting, use {@link #ofNative(double)} that returns a {@code Per} instance directly.
|
|
*
|
|
* @param magnitude the magnitude of the measure
|
|
* @return the ratio measure
|
|
*/
|
|
@Override
|
|
public Measure<? extends PerUnit<N, D>> of(double magnitude) {
|
|
return ofNative(magnitude);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* <p>Note: When called on an object of type {@code PerUnit} (and <i>not</i> a subclass!), this
|
|
* method will always return a {@link edu.wpi.first.units.measure.Per} instance. If you want to
|
|
* avoid casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance
|
|
* directly.
|
|
*
|
|
* @param baseUnitMagnitude the magnitude of the measure in terms of its base units.
|
|
* @return the ratio measure
|
|
*/
|
|
@Override
|
|
public Measure<? extends PerUnit<N, D>> ofBaseUnits(double baseUnitMagnitude) {
|
|
return ofNativeBaseUnits(baseUnitMagnitude);
|
|
}
|
|
|
|
/**
|
|
* Creates a new immutable measurement of the given magnitude in terms of the ratio unit. This
|
|
* will always return a {@code Per} object and cannot be overridden by subclasses.
|
|
*
|
|
* @param magnitude the magnitude of the measurement.
|
|
* @return the measurement object
|
|
* @see #of(double)
|
|
*/
|
|
public final Per<N, D> ofNative(double magnitude) {
|
|
return new ImmutablePer<>(magnitude, toBaseUnits(magnitude), this);
|
|
}
|
|
|
|
/**
|
|
* Creates a new immutable measurement of the given magnitude in terms of the ratio unit's base
|
|
* unit. This will always return a {@code Per} object and cannot be overridden by subclasses.
|
|
*
|
|
* @param baseUnitMagnitude the magnitude of the measure in terms of its base units.
|
|
* @return the measurement object
|
|
* @see #ofBaseUnits(double)
|
|
*/
|
|
public final Per<N, D> ofNativeBaseUnits(double baseUnitMagnitude) {
|
|
return new ImmutablePer<>(fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, this);
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("unchecked")
|
|
public Measure<? extends PerUnit<N, D>> zero() {
|
|
return (Measure<? extends PerUnit<N, D>>) super.zero();
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("unchecked")
|
|
public Measure<? extends PerUnit<N, D>> one() {
|
|
return (Measure<? extends PerUnit<N, D>>) super.one();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* <p>Note: When called on an object of type {@code PerUnit} (and <i>not</i> a subclass!), this
|
|
* method will always return a {@link edu.wpi.first.units.measure.MutPer} instance.
|
|
*
|
|
* @param initialMagnitude the starting magnitude of the measure
|
|
* @return the ratio measure
|
|
*/
|
|
@Override
|
|
public MutableMeasure<? extends PerUnit<N, D>, ?, ?> mutable(double initialMagnitude) {
|
|
return mutableNative(initialMagnitude);
|
|
}
|
|
|
|
/**
|
|
* Creates a new mutable measurement of the given magnitude in terms of the ratio unit. This will
|
|
* always return a {@code Per} object and cannot be overridden by subclasses.
|
|
*
|
|
* @param initialMagnitude the starting magnitude of the measure
|
|
* @return the ratio measure
|
|
* @see #mutable(double)
|
|
*/
|
|
public final MutPer<N, D> mutableNative(double initialMagnitude) {
|
|
return new MutPer<>(initialMagnitude, toBaseUnits(initialMagnitude), this);
|
|
}
|
|
|
|
@Override
|
|
public Unit per(TimeUnit time) {
|
|
return VelocityUnit.combine(this, time);
|
|
}
|
|
|
|
/**
|
|
* Converts a measurement value in terms of another unit to this unit.
|
|
*
|
|
* @param magnitude the magnitude of the measurement in terms of the other unit
|
|
* @param otherUnit the other unit
|
|
* @return the value of the measurement in terms of this unit
|
|
*/
|
|
public double convertFrom(double magnitude, PerUnit<? extends N, ? extends D> otherUnit) {
|
|
return fromBaseUnits(otherUnit.toBaseUnits(magnitude));
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (this == o) {
|
|
return true;
|
|
}
|
|
if (o == null || getClass() != o.getClass()) {
|
|
return false;
|
|
}
|
|
if (!super.equals(o)) {
|
|
return false;
|
|
}
|
|
PerUnit<?, ?> perUnit = (PerUnit<?, ?>) o;
|
|
return Objects.equals(m_numerator, perUnit.m_numerator)
|
|
&& Objects.equals(m_denominator, perUnit.m_denominator);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(super.hashCode(), m_numerator, m_denominator);
|
|
}
|
|
}
|