// 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 the type of the numerator unit * @param the type of the denominator unit */ public class PerUnit 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 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 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 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. * *
   *   PerUnit.combine(Volts, Meters) // possible PID constant
   * 
* * @param the type of the numerator unit * @param 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 PerUnit combine(N numerator, D denominator) { return cache.combine(numerator, denominator); } @Override @SuppressWarnings("unchecked") public PerUnit getBaseUnit() { return (PerUnit) super.getBaseUnit(); } /** * Gets the numerator unit. For a {@code PerUnit}, 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}, 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 reciprocal() { if (m_numerator instanceof TimeUnit t) { // Dividing by time, return a velocity return (PerUnit) 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} * *

Note: When called on an object of type {@code PerUnit} (and not 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> of(double magnitude) { return ofNative(magnitude); } /** * {@inheritDoc} * *

Note: When called on an object of type {@code PerUnit} (and not 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> 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 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 ofNativeBaseUnits(double baseUnitMagnitude) { return new ImmutablePer<>(fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, this); } @Override @SuppressWarnings("unchecked") public Measure> zero() { return (Measure>) super.zero(); } @Override @SuppressWarnings("unchecked") public Measure> one() { return (Measure>) super.one(); } /** * {@inheritDoc} * *

Note: When called on an object of type {@code PerUnit} (and not 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, ?, ?> 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 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 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); } }