001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.units;
006
007import java.util.Objects;
008
009/**
010 * Unit of measurement that defines a quantity, such as grams, meters, or seconds.
011 *
012 * <p>This is the base class for units. Actual units (such as {@link Units#Grams} and {@link
013 * Units#Meters}) can be found in the {@link Units} class.
014 *
015 * @param <U> the self type, e.g. {@code class SomeUnit extends Unit<SomeUnit>}
016 */
017public class Unit<U extends Unit<U>> {
018  private final UnaryFunction m_toBaseConverter;
019  private final UnaryFunction m_fromBaseConverter;
020
021  final Class<? extends U> m_baseType; // package-private for the builder
022
023  private Measure<U> m_zero;
024  private Measure<U> m_one;
025
026  private final String m_name;
027  private final String m_symbol;
028
029  /**
030   * Creates a new unit defined by its relationship to some base unit.
031   *
032   * @param baseType the base type of the unit, e.g. Distance.class for the distance unit
033   * @param toBaseConverter a function for converting units of this type to the base unit
034   * @param fromBaseConverter a function for converting units of the base unit to this one
035   * @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters")
036   * @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds
037   */
038  protected Unit(
039      Class<? extends U> baseType,
040      UnaryFunction toBaseConverter,
041      UnaryFunction fromBaseConverter,
042      String name,
043      String symbol) {
044    m_baseType = Objects.requireNonNull(baseType);
045    m_toBaseConverter = Objects.requireNonNull(toBaseConverter);
046    m_fromBaseConverter = Objects.requireNonNull(fromBaseConverter);
047    m_name = Objects.requireNonNull(name);
048    m_symbol = Objects.requireNonNull(symbol);
049  }
050
051  /**
052   * Creates a new unit with the given name and multiplier to the base unit.
053   *
054   * @param baseType the base type of the unit, e.g. Distance.class for the distance unit
055   * @param baseUnitEquivalent the multiplier to convert this unit to the base unit of this type.
056   *     For example, meters has a multiplier of 1, mm has a multiplier of 1e3, and km has
057   *     multiplier of 1e-3.
058   * @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters")
059   * @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds
060   */
061  protected Unit(
062      Class<? extends U> baseType, double baseUnitEquivalent, String name, String symbol) {
063    this(baseType, x -> x * baseUnitEquivalent, x -> x / baseUnitEquivalent, name, symbol);
064  }
065
066  /**
067   * Converts a value in terms of base units to a value in terms of this unit.
068   *
069   * @param valueInBaseUnits the value in base units to convert
070   * @return the equivalent value in terms of this unit
071   */
072  public double fromBaseUnits(double valueInBaseUnits) {
073    return m_fromBaseConverter.apply(valueInBaseUnits);
074  }
075
076  /**
077   * Converts a value in terms of this unit to a value in terms of the base unit.
078   *
079   * @param valueInNativeUnits the value in terms of this unit to convert
080   * @return the equivalent value in terms of the base unit
081   */
082  public double toBaseUnits(double valueInNativeUnits) {
083    return m_toBaseConverter.apply(valueInNativeUnits);
084  }
085
086  /**
087   * Converts a magnitude in terms of another unit of the same dimension to a magnitude in terms of
088   * this unit.
089   *
090   * <pre>
091   *   Inches.convertFrom(12, Feet) // 144.0
092   *   Kilograms.convertFrom(2.2, Pounds) // 0.9979024
093   * </pre>
094   *
095   * @param magnitude a magnitude measured in another unit
096   * @param otherUnit the unit to convert the magnitude to
097   * @return the corresponding value in terms of this unit.
098   */
099  public double convertFrom(double magnitude, Unit<U> otherUnit) {
100    if (this.equivalent(otherUnit)) {
101      // same unit, don't bother converting
102      return magnitude;
103    }
104    return this.fromBaseUnits(otherUnit.toBaseUnits(magnitude));
105  }
106
107  /**
108   * Gets the conversion function used to convert values to base unit terms. This generally
109   * shouldn't need to be used directly; prefer {@link #toBaseUnits(double)} instead.
110   *
111   * @return the conversion function
112   */
113  public UnaryFunction getConverterToBase() {
114    return m_toBaseConverter;
115  }
116
117  /**
118   * Gets the conversion function used to convert values to terms of this unit. This generally
119   * shouldn't need to be used directly; prefer {@link #fromBaseUnits(double)} instead.
120   *
121   * @return the conversion function
122   */
123  public UnaryFunction getConverterFromBase() {
124    return m_fromBaseConverter;
125  }
126
127  /**
128   * Creates a new measure of this unit with the given value. The resulting measure is
129   * <i>immutable</i> and cannot have its value modified.
130   *
131   * @param magnitude the magnitude of the measure to create
132   * @return the measure
133   */
134  public Measure<U> of(double magnitude) {
135    if (magnitude == 0) {
136      // reuse static object
137      return zero();
138    }
139    if (magnitude == 1) {
140      // reuse static object
141      return one();
142    }
143    return ImmutableMeasure.ofRelativeUnits(magnitude, this);
144  }
145
146  /**
147   * Creates a new measure with a magnitude equal to the given base unit magnitude, converted to be
148   * in terms of this unit.
149   *
150   * @param baseUnitMagnitude the magnitude of the measure in terms of the base unit
151   * @return the measure
152   */
153  public Measure<U> ofBaseUnits(double baseUnitMagnitude) {
154    return ImmutableMeasure.ofBaseUnits(baseUnitMagnitude, this);
155  }
156
157  /**
158   * Gets a measure with a magnitude of 0 in terms of this unit.
159   *
160   * @return the zero-valued measure
161   */
162  public Measure<U> zero() {
163    // lazy init because 'this' is null in object initialization
164    if (m_zero == null) {
165      m_zero = ImmutableMeasure.ofRelativeUnits(0, this);
166    }
167    return m_zero;
168  }
169
170  /**
171   * Gets a measure with a magnitude of 1 in terms of this unit.
172   *
173   * @return the 1-valued measure
174   */
175  public Measure<U> one() {
176    // lazy init because 'this' is null in object initialization
177    if (m_one == null) {
178      m_one = ImmutableMeasure.ofRelativeUnits(1, this);
179    }
180    return m_one;
181  }
182
183  /**
184   * Creates a velocity unit derived from this one. Can be chained to denote velocity, acceleration,
185   * jerk, etc.
186   *
187   * <pre>
188   *   Meters.per(Second) // linear velocity
189   *   Kilograms.per(Second) // mass flow
190   *   Feet.per(Second).per(Second).of(32) // roughly 1G of acceleration
191   * </pre>
192   *
193   * @param period the time period of the velocity, such as seconds or milliseconds
194   * @return a velocity unit corresponding to the rate of change of this unit over time
195   */
196  public Velocity<U> per(Time period) {
197    return Velocity.combine(this, period);
198  }
199
200  /**
201   * Takes this unit and creates a new proportional unit where this unit is the numerator and the
202   * given denominator is the denominator.
203   *
204   * <pre>
205   *   Volts.per(Meter) // V/m
206   * </pre>
207   *
208   * @param <D> the type of the denominator units
209   * @param denominator the denominator of the proportional unit
210   * @return a combined proportional unit
211   */
212  @SuppressWarnings("unchecked")
213  public <D extends Unit<D>> Per<U, D> per(D denominator) {
214    return Per.combine((U) this, denominator);
215  }
216
217  /**
218   * Takes this unit and creates a new combinatory unit equivalent to this unit multiplied by
219   * another.
220   *
221   * <pre>
222   *   Volts.mult(Meter) // V*m
223   * </pre>
224   *
225   * @param <U2> the type of the unit to multiply by
226   * @param other the unit to multiply by
227   * @return a combined unit equivalent to this unit multiplied by the other
228   */
229  @SuppressWarnings("unchecked")
230  public <U2 extends Unit<U2>> Mult<U, U2> mult(U2 other) {
231    return Mult.combine((U) this, other);
232  }
233
234  /**
235   * Checks if this unit is equivalent to another one. Equivalence is determined by both units
236   * having the same base type and treat the same base unit magnitude as the same magnitude in their
237   * own units, to within {@link Measure#EQUIVALENCE_THRESHOLD}.
238   *
239   * @param other the unit to compare to.
240   * @return true if both units are equivalent, false if not
241   */
242  public boolean equivalent(Unit<?> other) {
243    if (this.m_baseType != other.m_baseType) {
244      // different unit types, not compatible
245      return false;
246    }
247
248    double arbitrary = 16_777.214; // 2^24 / 1e3
249
250    return Math.abs(
251                this.m_fromBaseConverter.apply(arbitrary)
252                    - other.m_fromBaseConverter.apply(arbitrary))
253            <= Measure.EQUIVALENCE_THRESHOLD
254        && Math.abs(
255                this.m_toBaseConverter.apply(arbitrary) - other.m_toBaseConverter.apply(arbitrary))
256            <= Measure.EQUIVALENCE_THRESHOLD;
257  }
258
259  @Override
260  public boolean equals(Object o) {
261    if (this == o) {
262      return true;
263    }
264    if (!(o instanceof Unit)) {
265      return false;
266    }
267    Unit<?> that = (Unit<?>) o;
268    return m_baseType.equals(that.m_baseType)
269        && m_name.equals(that.m_name)
270        && m_symbol.equals(that.m_symbol)
271        && this.equivalent(that);
272  }
273
274  @Override
275  public int hashCode() {
276    return Objects.hash(m_toBaseConverter, m_fromBaseConverter, m_baseType, m_name, m_symbol);
277  }
278
279  /**
280   * Gets the name of this unit.
281   *
282   * @return the unit's name
283   */
284  public String name() {
285    return m_name;
286  }
287
288  /**
289   * Gets the symbol of this unit.
290   *
291   * @return the unit's symbol
292   */
293  public String symbol() {
294    return m_symbol;
295  }
296
297  @Override
298  public String toString() {
299    return name();
300  }
301}