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 edu.wpi.first.units.collections.LongToObjectHashMap;
008import java.util.Objects;
009
010/**
011 * Unit of velocity dimension that is a combination of a distance unit (numerator) and a time unit
012 * (denominator).
013 *
014 * <p>This is the base type for units of velocity dimension. It is also used in combination with a
015 * distance dimension to specify the dimension for {@link Measure}. For example: <code>
016 * Measure&lt;Velocity&lt;Distance&gt;&gt;</code>.
017 *
018 * <p>Actual units (such as {@link Units#MetersPerSecond} and {@link Units#RPM}) can be found in the
019 * {@link Units} class.
020 *
021 * @param <D> the distance unit, such as {@link Angle} or {@link Distance}
022 */
023public class Velocity<D extends Unit<D>> extends Unit<Velocity<D>> {
024  private final D m_unit;
025  private final Time m_period;
026
027  /**
028   * Stores velocity units that were created ad-hoc using {@link #combine(Unit, Time, String,
029   * String)}. Does not store objects created directly by constructors.
030   */
031  @SuppressWarnings("rawtypes")
032  private static final LongToObjectHashMap<Velocity> cache = new LongToObjectHashMap<>();
033
034  /** Generates a cache key used for cache lookups. */
035  private static long cacheKey(Unit<?> numerator, Unit<?> denominator) {
036    return ((long) numerator.hashCode()) << 32L | (((long) denominator.hashCode()) & 0xFFFFFFFFL);
037  }
038
039  /**
040   * Creates a new velocity unit derived from an arbitrary numerator and time period units.
041   *
042   * <p>Results of this method are cached so future invocations with the same arguments will return
043   * the pre-existing units instead of generating new identical ones.
044   *
045   * <pre>
046   *   Velocity.combine(Kilograms, Second) // mass flow
047   *   Velocity.combine(Feet, Millisecond) // linear speed
048   *   Velocity.combine(Radians, Second) // angular speed
049   *
050   *   Velocity.combine(Feet.per(Second), Second) // linear acceleration in ft/s/s
051   *   Velocity.combine(Radians.per(Second), Second) // angular acceleration
052   * </pre>
053   *
054   * <p>It's recommended to use the convenience function {@link Unit#per(Time)} instead of calling
055   * this factory directly.
056   *
057   * @param <D> the type of the numerator unit
058   * @param numerator the numerator unit
059   * @param period the period for unit time
060   * @param name the name of the new velocity unit
061   * @param symbol the symbol of the new velocity unit
062   * @return the new unit
063   */
064  @SuppressWarnings("unchecked")
065  public static <D extends Unit<D>> Velocity<D> combine(
066      Unit<D> numerator, Time period, String name, String symbol) {
067    long key = cacheKey(numerator, period);
068    if (cache.containsKey(key)) {
069      return cache.get(key);
070    }
071
072    Velocity<D> velocity = new Velocity<>((D) numerator, period, name, symbol);
073    cache.put(key, velocity);
074    return velocity;
075  }
076
077  /**
078   * Creates a new velocity unit derived from an arbitrary numerator and time period units.
079   *
080   * <p>Results of this method are cached so future invocations with the same arguments will return
081   * the pre-existing units instead of generating new identical ones.
082   *
083   * <p>This method automatically generates a new name and symbol for the new velocity unit.
084   *
085   * <pre>
086   *   Velocity.combine(Kilograms, Second) // mass flow
087   *   Velocity.combine(Feet, Millisecond) // linear speed
088   *   Velocity.combine(Radians, Second) // angular speed
089   *
090   *   Velocity.combine(Feet.per(Second), Second) // linear acceleration in ft/s/s
091   *   Velocity.combine(Radians.per(Second), Second) // angular acceleration
092   * </pre>
093   *
094   * <p>It's recommended to use the convenience function {@link Unit#per(Time)} instead of calling
095   * this factory directly.
096   *
097   * @param <D> the type of the numerator unit
098   * @param numerator the numerator unit
099   * @param period the period for unit time
100   * @return the new unit
101   */
102  @SuppressWarnings("unchecked")
103  public static <D extends Unit<D>> Velocity<D> combine(Unit<D> numerator, Time period) {
104    long key = cacheKey(numerator, period);
105    if (cache.containsKey(key)) {
106      return cache.get(key);
107    }
108
109    var name = numerator.name() + " per " + period.name();
110    var symbol = numerator.symbol() + "/" + period.symbol();
111
112    Velocity<D> velocity = new Velocity<>((D) numerator, period, name, symbol);
113    cache.put(key, velocity);
114    return velocity;
115  }
116
117  @SuppressWarnings({"unchecked", "rawtypes"})
118  Velocity(D unit, Time period, String name, String symbol) {
119    super((Class) Velocity.class, unit.toBaseUnits(1) / period.toBaseUnits(1), name, symbol);
120    this.m_unit = unit;
121    this.m_period = period;
122  }
123
124  @SuppressWarnings({"unchecked", "rawtypes"})
125  Velocity(
126      UnaryFunction toBaseConverter, UnaryFunction fromBaseConverter, String name, String symbol) {
127    super((Class) Velocity.class, toBaseConverter, fromBaseConverter, name, symbol);
128    this.m_unit = Units.anonymous();
129    this.m_period = Units.Seconds;
130  }
131
132  /**
133   * Gets the major unit being measured (eg Meters for Meters per Second).
134   *
135   * @return the major unit
136   */
137  public D getUnit() {
138    return m_unit;
139  }
140
141  /**
142   * Gets the period unit of the velocity, eg Seconds or Milliseconds.
143   *
144   * @return the period unit
145   */
146  public Time getPeriod() {
147    return m_period;
148  }
149
150  @Override
151  public boolean equals(Object o) {
152    if (this == o) {
153      return true;
154    }
155    if (o == null || getClass() != o.getClass()) {
156      return false;
157    }
158    if (!super.equals(o)) {
159      return false;
160    }
161    Velocity<?> velocity = (Velocity<?>) o;
162    return m_unit.equals(velocity.m_unit) && m_period.equals(velocity.m_period);
163  }
164
165  @Override
166  public int hashCode() {
167    return Objects.hash(super.hashCode(), m_unit, m_period);
168  }
169}