From 6c9dcc157e7b1ac7dfbd7fc96194ab810229c8a5 Mon Sep 17 00:00:00 2001 From: Sam Carlberg Date: Sun, 12 May 2024 09:15:56 -0400 Subject: [PATCH] [wpiunits] Change units to track their base unit, instead of their base class (#6342) Unit objects now have a reference to the base unit from which they're derived. Constructing a unit object without specifying a base unit implicitly signifies that it's its own base unit, eg new Angle(null, 1, "Radian", "rad") would be the base angle unit of radians, while new Angle(Radians, 2 * PI, "Rotation", "R") would be a new angle unit based on radians. This fixes much of the hacky code surrounding the derived unit types Velocity, Per, and Mult, but is a breaking change for any user code that defines custom unit classes or uses the anonymous unit type. --- .../main/java/edu/wpi/first/units/Angle.java | 19 ++- .../java/edu/wpi/first/units/BaseUnits.java | 25 ++-- .../java/edu/wpi/first/units/Current.java | 16 ++- .../edu/wpi/first/units/Dimensionless.java | 12 +- .../java/edu/wpi/first/units/Distance.java | 13 +- .../main/java/edu/wpi/first/units/Energy.java | 12 +- .../main/java/edu/wpi/first/units/Mass.java | 13 +- .../java/edu/wpi/first/units/Measure.java | 20 +-- .../main/java/edu/wpi/first/units/Mult.java | 18 ++- .../main/java/edu/wpi/first/units/Per.java | 22 ++- .../main/java/edu/wpi/first/units/Power.java | 12 +- .../java/edu/wpi/first/units/Temperature.java | 8 +- .../main/java/edu/wpi/first/units/Time.java | 14 +- .../edu/wpi/first/units/UnaryFunction.java | 3 + .../main/java/edu/wpi/first/units/Unit.java | 53 +++++-- .../java/edu/wpi/first/units/UnitBuilder.java | 132 +++++++++++++----- .../main/java/edu/wpi/first/units/Units.java | 24 +--- .../java/edu/wpi/first/units/Velocity.java | 20 ++- .../java/edu/wpi/first/units/Voltage.java | 16 ++- .../edu/wpi/first/units/DistanceTest.java | 5 +- .../java/edu/wpi/first/units/ExampleUnit.java | 11 +- .../java/edu/wpi/first/units/UnitsTest.java | 2 +- .../edu/wpi/first/units/VelocityTest.java | 4 +- 23 files changed, 312 insertions(+), 162 deletions(-) diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Angle.java b/wpiunits/src/main/java/edu/wpi/first/units/Angle.java index 8fd20bb606..f20d98e702 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Angle.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Angle.java @@ -17,19 +17,16 @@ package edu.wpi.first.units; // eg Mass * Distance * Velocity is equivalent to (Mass * Distance) / Time - otherwise known // as Power - in other words, Velocity is /actually/ Frequency public class Angle extends Unit { - /** - * Creates a new unit with the given name and multiplier to the base unit. - * - * @param baseUnitEquivalent the multiplier to convert this unit to the base unit of this type - * @param name the name of the angle measure - * @param symbol the symbol of the angle measure - */ - Angle(double baseUnitEquivalent, String name, String symbol) { - super(Angle.class, baseUnitEquivalent, name, symbol); + Angle(Angle baseUnit, double baseUnitEquivalent, String name, String symbol) { + super(baseUnit, baseUnitEquivalent, name, symbol); } Angle( - UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(Angle.class, toBaseConverter, fromBaseConverter, name, symbol); + Angle baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/BaseUnits.java b/wpiunits/src/main/java/edu/wpi/first/units/BaseUnits.java index 28a36bf79f..ed10d0b388 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/BaseUnits.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/BaseUnits.java @@ -11,36 +11,33 @@ public final class BaseUnits { } /** The standard unit of distance, meters. */ - public static final Distance Distance = new Distance(1, "Meter", "m"); + public static final Distance Distance = new Distance(null, 1, "Meter", "m"); /** The standard unit of time, seconds. */ - public static final Time Time = new Time(1, "Second", "s"); - - /** The standard unit of velocity, meters per second. */ - public static final Velocity Velocity = - new Velocity<>(Distance, Time, "Meter per Second", "m/s"); + public static final Time Time = new Time(null, 1, "Second", "s"); /** The standard unit of mass, kilograms. */ - public static final Mass Mass = new Mass(1, "Kilogram", "Kg"); + public static final Mass Mass = new Mass(null, 1, "Kilogram", "Kg"); /** The standard unit of angles, radians. */ - public static final Angle Angle = new Angle(1, "Radian", "rad"); + public static final Angle Angle = new Angle(null, 1, "Radian", "rad"); /** The standard "unitless" unit. */ - public static final Dimensionless Value = new Dimensionless(1, "", ""); + public static final Dimensionless Value = new Dimensionless(null, 1, "", ""); /** The standard unit of voltage, volts. */ - public static final Voltage Voltage = new Voltage(1, "Volt", "V"); + public static final Voltage Voltage = new Voltage(null, 1, "Volt", "V"); /** The standard unit of electric current, amperes. */ - public static final Current Current = new Current(1, "Amp", "A"); + public static final Current Current = new Current(null, 1, "Amp", "A"); /** The standard unit of energy, joules. */ - public static final Energy Energy = new Energy(1, "Joule", "J"); + public static final Energy Energy = new Energy(null, 1, "Joule", "J"); /** The standard unit of power, watts. */ - public static final Power Power = new Power(1, "Watt", "W"); + public static final Power Power = new Power(null, 1, "Watt", "W"); /** The standard unit of temperature, kelvin. */ - public static final Temperature Temperature = new Temperature(x -> x, x -> x, "Kelvin", "K"); + public static final Temperature Temperature = + new Temperature(null, x -> x, x -> x, "Kelvin", "K"); } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Current.java b/wpiunits/src/main/java/edu/wpi/first/units/Current.java index 744164c016..b5d25ac070 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Current.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Current.java @@ -4,6 +4,8 @@ package edu.wpi.first.units; +import static edu.wpi.first.units.Units.Watts; + /** * Unit of electic current dimension. * @@ -14,13 +16,17 @@ package edu.wpi.first.units; * {@link Units} class. */ public class Current extends Unit { - Current(double baseUnitEquivalent, String name, String symbol) { - super(Current.class, baseUnitEquivalent, name, symbol); + Current(Current baseUnit, double baseUnitEquivalent, String name, String symbol) { + super(baseUnit, baseUnitEquivalent, name, symbol); } Current( - UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(Current.class, toBaseConverter, fromBaseConverter, name, symbol); + Current baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); } /** @@ -35,6 +41,6 @@ public class Current extends Unit { * @return the power unit */ public Power times(Unit voltage, String name, String symbol) { - return new Power(this.toBaseUnits(1) * voltage.toBaseUnits(1), name, symbol); + return new Power(Watts, this.toBaseUnits(1) * voltage.toBaseUnits(1), name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java b/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java index 4fdef64f40..6a07f38e90 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Dimensionless.java @@ -15,12 +15,16 @@ public class Dimensionless extends Unit { * @param name the name of the unit * @param symbol the symbol of the unit */ - Dimensionless(double baseUnitEquivalent, String name, String symbol) { - super(Dimensionless.class, baseUnitEquivalent, name, symbol); + Dimensionless(Dimensionless baseUnit, double baseUnitEquivalent, String name, String symbol) { + super(baseUnit, baseUnitEquivalent, name, symbol); } Dimensionless( - UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(Dimensionless.class, toBaseConverter, fromBaseConverter, name, symbol); + Dimensionless baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Distance.java b/wpiunits/src/main/java/edu/wpi/first/units/Distance.java index 7a0336f844..b8859b3ca1 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Distance.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Distance.java @@ -14,13 +14,16 @@ package edu.wpi.first.units; * {@link Units} class. */ public class Distance extends Unit { - /** Creates a new unit with the given name and multiplier to the base unit. */ - Distance(double baseUnitEquivalent, String name, String symbol) { - super(Distance.class, baseUnitEquivalent, name, symbol); + Distance(Distance baseUnit, double baseUnitEquivalent, String name, String symbol) { + super(baseUnit, baseUnitEquivalent, name, symbol); } Distance( - UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(Distance.class, toBaseConverter, fromBaseConverter, name, symbol); + Distance baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Energy.java b/wpiunits/src/main/java/edu/wpi/first/units/Energy.java index 55a9eaf62d..dc5be8de17 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Energy.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Energy.java @@ -15,11 +15,15 @@ package edu.wpi.first.units; */ public class Energy extends Unit { Energy( - UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(Energy.class, toBaseConverter, fromBaseConverter, name, symbol); + Energy baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); } - Energy(double baseUnitEquivalent, String name, String symbol) { - super(Energy.class, baseUnitEquivalent, name, symbol); + Energy(Energy baseUnit, double baseUnitEquivalent, String name, String symbol) { + super(baseUnit, baseUnitEquivalent, name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Mass.java b/wpiunits/src/main/java/edu/wpi/first/units/Mass.java index eb4c45d6c8..5d41753603 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Mass.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Mass.java @@ -15,11 +15,16 @@ package edu.wpi.first.units; */ public class Mass extends Unit { /** Creates a new unit with the given name and multiplier to the base unit. */ - Mass(double baseUnitEquivalent, String name, String symbol) { - super(Mass.class, baseUnitEquivalent, name, symbol); + Mass(Mass baseUnit, double baseUnitEquivalent, String name, String symbol) { + super(baseUnit, baseUnitEquivalent, name, symbol); } - Mass(UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(Mass.class, toBaseConverter, fromBaseConverter, name, symbol); + Mass( + Mass baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Measure.java b/wpiunits/src/main/java/edu/wpi/first/units/Measure.java index 41036b0e8e..eb5b7a07ae 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Measure.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Measure.java @@ -4,6 +4,8 @@ package edu.wpi.first.units; +import static edu.wpi.first.units.Units.Seconds; + /** * A measure holds the magnitude and unit of some dimension, such as distance, time, or speed. Two * measures with the same unit and magnitude are effectively equivalent objects. @@ -91,28 +93,28 @@ public interface Measure> extends Comparable> { } if (unit() instanceof Per - && other.unit().m_baseType.equals(((Per) unit()).denominator().m_baseType)) { + && other.unit().getBaseUnit().equals(((Per) unit()).denominator().getBaseUnit())) { // denominator of the Per cancels out, return with just the units of the numerator Unit numerator = ((Per) unit()).numerator(); return numerator.ofBaseUnits(baseUnitMagnitude() * other.baseUnitMagnitude()); - } else if (unit() instanceof Velocity && other.unit().m_baseType.equals(Time.class)) { + } else if (unit() instanceof Velocity && other.unit().getBaseUnit().equals(Seconds)) { // Multiplying a velocity by a time, return the scalar unit (eg Distance) Unit numerator = ((Velocity) unit()).getUnit(); return numerator.ofBaseUnits(baseUnitMagnitude() * other.baseUnitMagnitude()); } else if (other.unit() instanceof Per - && unit().m_baseType.equals(((Per) other.unit()).denominator().m_baseType)) { + && unit().getBaseUnit().equals(((Per) other.unit()).denominator().getBaseUnit())) { Unit numerator = ((Per) other.unit()).numerator(); return numerator.ofBaseUnits(baseUnitMagnitude() * other.baseUnitMagnitude()); } else if (unit() instanceof Per && other.unit() instanceof Per && ((Per) unit()) .denominator() - .m_baseType - .equals(((Per) other.unit()).numerator().m_baseType) + .getBaseUnit() + .equals(((Per) other.unit()).numerator().getBaseUnit()) && ((Per) unit()) .numerator() - .m_baseType - .equals(((Per) other.unit()).denominator().m_baseType)) { + .getBaseUnit() + .equals(((Per) other.unit()).denominator().getBaseUnit())) { // multiplying eg meters per second * milliseconds per foot // return a scalar return Units.Value.of(baseUnitMagnitude() * other.baseUnitMagnitude()); @@ -255,7 +257,7 @@ public interface Measure> extends Comparable> { * @return true if this unit is near the other measure, otherwise false */ default boolean isNear(Measure other, double varianceThreshold) { - if (this.unit().m_baseType != other.unit().m_baseType) { + if (!this.unit().getBaseUnit().equivalent(other.unit().getBaseUnit())) { return false; // Disjoint units, not compatible } @@ -291,7 +293,7 @@ public interface Measure> extends Comparable> { * @return true if this measure is equivalent, false otherwise */ default boolean isEquivalent(Measure other) { - if (this.unit().m_baseType != other.unit().m_baseType) { + if (!this.unit().getBaseUnit().equals(other.unit().getBaseUnit())) { return false; // Disjoint units, not compatible } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Mult.java b/wpiunits/src/main/java/edu/wpi/first/units/Mult.java index 9a8c33a34c..d5e54d0e52 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Mult.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Mult.java @@ -26,13 +26,12 @@ public class Mult, B extends Unit> extends Unit> * Creates a new product unit. Consider using {@link #combine} instead of manually calling this * constructor. * - * @param baseType the base type representing the unit product * @param a the first unit of the product * @param b the second unit of the product */ - protected Mult(Class> baseType, A a, B b) { + protected Mult(A a, B b) { super( - baseType, + a.isBaseUnit() && b.isBaseUnit() ? null : combine(a.getBaseUnit(), b.getBaseUnit()), a.toBaseUnits(1) * b.toBaseUnits(1), a.name() + "-" + b.name(), a.symbol() + "*" + b.symbol()); @@ -40,6 +39,17 @@ public class Mult, B extends Unit> extends Unit> m_unitB = b; } + Mult( + Mult baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); + m_unitA = baseUnit.unitA(); + m_unitB = baseUnit.unitB(); + } + /** * Creates a new Mult unit derived from two arbitrary units multiplied together. * @@ -63,7 +73,7 @@ public class Mult, B extends Unit> extends Unit> return cache.get(key); } - var mult = new Mult((Class) Mult.class, a, b); + var mult = new Mult(a, b); cache.put(key, mult); return mult; } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Per.java b/wpiunits/src/main/java/edu/wpi/first/units/Per.java index cdb2fe37db..afde7a447f 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Per.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Per.java @@ -32,13 +32,14 @@ public class Per, D extends Unit> extends Unit> { * 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 baseType the base type representing the unit ratio * @param numerator the numerator unit * @param denominator the denominator unit */ - protected Per(Class> baseType, N numerator, D denominator) { + protected Per(N numerator, D denominator) { super( - baseType, + 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()); @@ -46,6 +47,17 @@ public class Per, D extends Unit> extends Unit> { m_denominator = denominator; } + Per( + Per baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); + m_numerator = baseUnit.numerator(); + m_denominator = baseUnit.denominator(); + } + /** * Creates a new Per unit derived from an arbitrary numerator and time denominator units. Using a * denominator with a unit of time is discouraged; use {@link Velocity} instead. @@ -63,7 +75,7 @@ public class Per, D extends Unit> extends Unit> { * @param denominator the denominator for unit time * @return the combined unit */ - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings("unchecked") public static , D extends Unit> Per combine( N numerator, D denominator) { final long key = @@ -74,7 +86,7 @@ public class Per, D extends Unit> extends Unit> { return existing; } - var newUnit = new Per((Class) Per.class, numerator, denominator); + var newUnit = new Per<>(numerator, denominator); cache.put(key, newUnit); return newUnit; } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Power.java b/wpiunits/src/main/java/edu/wpi/first/units/Power.java index 627f1cd874..5d43f144d6 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Power.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Power.java @@ -14,12 +14,16 @@ package edu.wpi.first.units; * {@link Units} class. */ public class Power extends Unit { - Power(double baseUnitEquivalent, String name, String symbol) { - super(Power.class, baseUnitEquivalent, name, symbol); + Power(Power baseUnit, double baseUnitEquivalent, String name, String symbol) { + super(baseUnit, baseUnitEquivalent, name, symbol); } Power( - UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(Power.class, toBaseConverter, fromBaseConverter, name, symbol); + Power baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Temperature.java b/wpiunits/src/main/java/edu/wpi/first/units/Temperature.java index c1f27f4f11..3138b1ac86 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Temperature.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Temperature.java @@ -15,7 +15,11 @@ package edu.wpi.first.units; */ public class Temperature extends Unit { Temperature( - UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) { - super(Temperature.class, toBaseConverter, fromBaseConverter, name, symbol); + Temperature baseUnit, + UnaryFunction toBaseConverter, + UnaryFunction fromBaseConverter, + String name, + String symbol) { + super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); } } diff --git a/wpiunits/src/main/java/edu/wpi/first/units/Time.java b/wpiunits/src/main/java/edu/wpi/first/units/Time.java index 858ef35236..5d1ec87e06 100644 --- a/wpiunits/src/main/java/edu/wpi/first/units/Time.java +++ b/wpiunits/src/main/java/edu/wpi/first/units/Time.java @@ -14,12 +14,16 @@ package edu.wpi.first.units; * the {@link Units} class. */ public class Time extends Unit