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 org.wpilib.units; 006 007import java.util.Objects; 008import org.wpilib.units.measure.Per; 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 org.wpilib.units.measure.Per} instance. If you want to avoid 161 * 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 org.wpilib.units.measure.Per} instance. If you want to avoid 176 * casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance directly. 177 * 178 * @param baseUnitMagnitude the magnitude of the measure in terms of its base units. 179 * @return the ratio measure 180 */ 181 @Override 182 public Measure<? extends PerUnit<N, D>> ofBaseUnits(double baseUnitMagnitude) { 183 return ofNativeBaseUnits(baseUnitMagnitude); 184 } 185 186 /** 187 * Creates a new immutable measurement of the given magnitude in terms of the ratio unit. This 188 * will always return a {@code Per} object and cannot be overridden by subclasses. 189 * 190 * @param magnitude the magnitude of the measurement. 191 * @return the measurement object 192 * @see #of(double) 193 */ 194 public final Per<N, D> ofNative(double magnitude) { 195 return new Per<>(magnitude, toBaseUnits(magnitude), this); 196 } 197 198 /** 199 * Creates a new immutable measurement of the given magnitude in terms of the ratio unit's base 200 * unit. This will always return a {@code Per} object and cannot be overridden by subclasses. 201 * 202 * @param baseUnitMagnitude the magnitude of the measure in terms of its base units. 203 * @return the measurement object 204 * @see #ofBaseUnits(double) 205 */ 206 public final Per<N, D> ofNativeBaseUnits(double baseUnitMagnitude) { 207 return new Per<>(fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, this); 208 } 209 210 @Override 211 @SuppressWarnings("unchecked") 212 public Measure<? extends PerUnit<N, D>> zero() { 213 return (Measure<? extends PerUnit<N, D>>) super.zero(); 214 } 215 216 @Override 217 @SuppressWarnings("unchecked") 218 public Measure<? extends PerUnit<N, D>> one() { 219 return (Measure<? extends PerUnit<N, D>>) super.one(); 220 } 221 222 @Override 223 public Unit per(TimeUnit time) { 224 return VelocityUnit.combine(this, time); 225 } 226 227 /** 228 * Converts a measurement value in terms of another unit to this unit. 229 * 230 * @param magnitude the magnitude of the measurement in terms of the other unit 231 * @param otherUnit the other unit 232 * @return the value of the measurement in terms of this unit 233 */ 234 public double convertFrom(double magnitude, PerUnit<? extends N, ? extends D> otherUnit) { 235 return fromBaseUnits(otherUnit.toBaseUnits(magnitude)); 236 } 237 238 @Override 239 public boolean equals(Object o) { 240 if (this == o) { 241 return true; 242 } 243 if (o == null || getClass() != o.getClass()) { 244 return false; 245 } 246 if (!super.equals(o)) { 247 return false; 248 } 249 PerUnit<?, ?> perUnit = (PerUnit<?, ?>) o; 250 return Objects.equals(m_numerator, perUnit.m_numerator) 251 && Objects.equals(m_denominator, perUnit.m_denominator); 252 } 253 254 @Override 255 public int hashCode() { 256 return Objects.hash(super.hashCode(), m_numerator, m_denominator); 257 } 258}