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.measure.Per; 008import java.util.Objects; 009 010/** 011 * Generic combinatory unit type that represents the proportion of one unit to another, such as 012 * Meters per Second or Radians per Celsius. 013 * 014 * @param <N> the type of the numerator unit 015 * @param <D> the type of the denominator unit 016 */ 017public class PerUnit<N extends Unit, D extends Unit> extends Unit { 018 private final N m_numerator; 019 private final D m_denominator; 020 021 /** 022 * Keep a cache of created instances so expressions like Volts.per(Meter) don't do any allocations 023 * after the first. 024 */ 025 @SuppressWarnings("rawtypes") 026 private static final CombinatoryUnitCache<Unit, Unit, PerUnit> cache = 027 new CombinatoryUnitCache<>(PerUnit::new); 028 029 /** 030 * Creates a new proportional unit derived from the ratio of one unit to another. Consider using 031 * {@link #combine} instead of manually calling this constructor. 032 * 033 * @param numerator the numerator unit 034 * @param denominator the denominator unit 035 */ 036 private PerUnit(N numerator, D denominator) { 037 super( 038 numerator.isBaseUnit() && denominator.isBaseUnit() 039 ? null 040 : combine(numerator.getBaseUnit(), denominator.getBaseUnit()), 041 numerator.toBaseUnits(1) / denominator.toBaseUnits(1), 042 numerator.name() + " per " + denominator.name(), 043 numerator.symbol() + "/" + denominator.symbol()); 044 m_numerator = numerator; 045 m_denominator = denominator; 046 } 047 048 /** 049 * Creates a new proportional unit derived from the ratio of one unit to another. Subclasses of 050 * {@code PerUnit} should use this constructor. 051 * 052 * @param baseUnit the base unit. Set this to null if the unit being constructed is its own base 053 * unit 054 * @param numerator the numerator unit 055 * @param denominator the denominator unit 056 */ 057 protected PerUnit(PerUnit<N, D> baseUnit, N numerator, D denominator) { 058 super( 059 baseUnit, 060 numerator.toBaseUnits(1) / denominator.toBaseUnits(1), 061 numerator.name() + " per " + denominator.name(), 062 numerator.symbol() + "/" + denominator.symbol()); 063 m_numerator = numerator; 064 m_denominator = denominator; 065 } 066 067 /** {@inheritDoc} */ 068 PerUnit( 069 PerUnit<N, D> baseUnit, 070 UnaryFunction toBaseConverter, 071 UnaryFunction fromBaseConverter, 072 String name, 073 String symbol) { 074 super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); 075 m_numerator = getBaseUnit().numerator(); 076 m_denominator = getBaseUnit().denominator(); 077 } 078 079 /** 080 * Creates a new PerUnit unit derived from an arbitrary numerator and time denominator units. 081 * 082 * <pre> 083 * PerUnit.combine(Volts, Meters) // possible PID constant 084 * </pre> 085 * 086 * @param <N> the type of the numerator unit 087 * @param <D> the type of the denominator unit 088 * @param numerator the numerator unit 089 * @param denominator the denominator for unit time 090 * @return the combined unit 091 */ 092 @SuppressWarnings("unchecked") 093 public static <N extends Unit, D extends Unit> PerUnit<N, D> combine(N numerator, D denominator) { 094 return cache.combine(numerator, denominator); 095 } 096 097 @Override 098 @SuppressWarnings("unchecked") 099 public PerUnit<N, D> getBaseUnit() { 100 return (PerUnit<N, D>) super.getBaseUnit(); 101 } 102 103 /** 104 * Gets the numerator unit. For a {@code PerUnit<A, B>}, this will return the {@code A} unit. 105 * 106 * @return the numerator unit 107 */ 108 public N numerator() { 109 return m_numerator; 110 } 111 112 /** 113 * Gets the denominator unit. For a {@code PerUnit<A, B>}, this will return the {@code B} unit. 114 * 115 * @return the denominator unit 116 */ 117 public D denominator() { 118 return m_denominator; 119 } 120 121 /** 122 * Returns the reciprocal of this PerUnit. 123 * 124 * @return the reciprocal 125 */ 126 @SuppressWarnings("unchecked") 127 public PerUnit<D, N> reciprocal() { 128 if (m_numerator instanceof TimeUnit t) { 129 // Dividing by time, return a velocity 130 return (PerUnit<D, N>) VelocityUnit.combine(m_denominator, t); 131 } else { 132 // Generic case 133 return combine(m_denominator, m_numerator); 134 } 135 } 136 137 /** 138 * Multiplies this unit by a unit of its denominator. 139 * 140 * @param denom the denominator-typed unit to multiply by 141 * @return the result 142 */ 143 public N mult(D denom) { 144 if (denom.equivalent(denominator())) { 145 return numerator(); 146 } 147 148 return Units.derive(numerator()) 149 .toBase(denom.getConverterToBase().div(denominator().getConverterToBase())) 150 .fromBase(denom.getConverterFromBase().div(denominator().getConverterFromBase())) 151 .named(name() + " " + denom.name()) 152 .symbol(symbol() + "-" + denom.symbol()) 153 .make(); 154 } 155 156 /** 157 * {@inheritDoc} 158 * 159 * <p>Note: When called on an object of type {@code PerUnit} (and <i>not</i> a subclass!), this 160 * method will always return a {@link edu.wpi.first.units.measure.Per} instance. If you want to 161 * avoid casting, use {@link #ofNative(double)} that returns a {@code Per} instance directly. 162 * 163 * @param magnitude the magnitude of the measure 164 * @return the ratio measure 165 */ 166 @Override 167 public Measure<? extends PerUnit<N, D>> of(double magnitude) { 168 return ofNative(magnitude); 169 } 170 171 /** 172 * {@inheritDoc} 173 * 174 * <p>Note: When called on an object of type {@code PerUnit} (and <i>not</i> a subclass!), this 175 * method will always return a {@link edu.wpi.first.units.measure.Per} instance. If you want to 176 * avoid casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance 177 * directly. 178 * 179 * @param baseUnitMagnitude the magnitude of the measure in terms of its base units. 180 * @return the ratio measure 181 */ 182 @Override 183 public Measure<? extends PerUnit<N, D>> ofBaseUnits(double baseUnitMagnitude) { 184 return ofNativeBaseUnits(baseUnitMagnitude); 185 } 186 187 /** 188 * Creates a new immutable measurement of the given magnitude in terms of the ratio unit. This 189 * will always return a {@code Per} object and cannot be overridden by subclasses. 190 * 191 * @param magnitude the magnitude of the measurement. 192 * @return the measurement object 193 * @see #of(double) 194 */ 195 public final Per<N, D> ofNative(double magnitude) { 196 return new Per<>(magnitude, toBaseUnits(magnitude), this); 197 } 198 199 /** 200 * Creates a new immutable measurement of the given magnitude in terms of the ratio unit's base 201 * unit. This will always return a {@code Per} object and cannot be overridden by subclasses. 202 * 203 * @param baseUnitMagnitude the magnitude of the measure in terms of its base units. 204 * @return the measurement object 205 * @see #ofBaseUnits(double) 206 */ 207 public final Per<N, D> ofNativeBaseUnits(double baseUnitMagnitude) { 208 return new Per<>(fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, this); 209 } 210 211 @Override 212 @SuppressWarnings("unchecked") 213 public Measure<? extends PerUnit<N, D>> zero() { 214 return (Measure<? extends PerUnit<N, D>>) super.zero(); 215 } 216 217 @Override 218 @SuppressWarnings("unchecked") 219 public Measure<? extends PerUnit<N, D>> one() { 220 return (Measure<? extends PerUnit<N, D>>) super.one(); 221 } 222 223 @Override 224 public Unit per(TimeUnit time) { 225 return VelocityUnit.combine(this, time); 226 } 227 228 /** 229 * Converts a measurement value in terms of another unit to this unit. 230 * 231 * @param magnitude the magnitude of the measurement in terms of the other unit 232 * @param otherUnit the other unit 233 * @return the value of the measurement in terms of this unit 234 */ 235 public double convertFrom(double magnitude, PerUnit<? extends N, ? extends D> otherUnit) { 236 return fromBaseUnits(otherUnit.toBaseUnits(magnitude)); 237 } 238 239 @Override 240 public boolean equals(Object o) { 241 if (this == o) { 242 return true; 243 } 244 if (o == null || getClass() != o.getClass()) { 245 return false; 246 } 247 if (!super.equals(o)) { 248 return false; 249 } 250 PerUnit<?, ?> perUnit = (PerUnit<?, ?>) o; 251 return Objects.equals(m_numerator, perUnit.m_numerator) 252 && Objects.equals(m_denominator, perUnit.m_denominator); 253 } 254 255 @Override 256 public int hashCode() { 257 return Objects.hash(super.hashCode(), m_numerator, m_denominator); 258 } 259}