View Javadoc
1   package org.djunits.quantity;
2   
3   import org.djunits.quantity.def.Quantity;
4   import org.djunits.unit.AbstractUnit;
5   import org.djunits.unit.UnitInterface;
6   import org.djunits.unit.UnitRuntimeException;
7   import org.djunits.unit.Unitless;
8   import org.djunits.unit.scale.GradeScale;
9   import org.djunits.unit.scale.IdentityScale;
10  import org.djunits.unit.scale.LinearScale;
11  import org.djunits.unit.scale.Scale;
12  import org.djunits.unit.si.SIPrefix;
13  import org.djunits.unit.si.SIUnit;
14  import org.djunits.unit.system.UnitSystem;
15  
16  /**
17   * Angle is the measure of rotation between two intersecting lines, expressed in radians (rad) or degrees.
18   * <p>
19   * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
20   * for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
21   * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
22   * @author Alexander Verbraeck
23   */
24  public class Angle extends Quantity<Angle>
25  {
26      /** Constant with value zero radians. */
27      public static final Angle ZERO = ofSi(0.0);
28  
29      /** Constant with value pi radians. */
30      public static final Angle PI = ofSi(Math.PI);
31  
32      /** Constant with value pi/2 radians. */
33      public static final Angle HALF_PI = ofSi(Math.PI / 2.0);
34  
35      /** Constant with value 2 pi radians. */
36      public static final Angle TWO_PI = ofSi(Math.PI * 2.0);
37  
38      /** Constant with value tau radians. */
39      public static final Angle TAU = ofSi(Math.PI * 2.0);
40  
41      /** Constant with value one radian. */
42      public static final Angle ONE = ofSi(1.0);
43  
44      /** Constant with value NaN. */
45      @SuppressWarnings("checkstyle:constantname")
46      public static final Angle NaN = ofSi(Double.NaN);
47  
48      /** Constant with value POSITIVE_INFINITY. */
49      public static final Angle POSITIVE_INFINITY = ofSi(Double.POSITIVE_INFINITY);
50  
51      /** Constant with value NEGATIVE_INFINITY. */
52      public static final Angle NEGATIVE_INFINITY = ofSi(Double.NEGATIVE_INFINITY);
53  
54      /** Constant with value MAX_VALUE. */
55      public static final Angle POS_MAXVALUE = ofSi(Double.MAX_VALUE);
56  
57      /** Constant with value -MAX_VALUE. */
58      public static final Angle NEG_MAXVALUE = ofSi(-Double.MAX_VALUE);
59  
60      /** */
61      private static final long serialVersionUID = 600L;
62  
63      /**
64       * Instantiate a Angle quantity with an SI or base value and a display unit.
65       * @param value the quantity value expressed in the SI or base unit
66       * @param displayUnit the display unit to use
67       * @param useSi use SI value when true, use value in unit when false
68       */
69      public Angle(final double value, final Angle.Unit displayUnit, final boolean useSi)
70      {
71          super(value, displayUnit, useSi);
72      }
73  
74      /**
75       * Instantiate a Angle quantity expressed in the given unit.
76       * @param valueInUnit the quantity value expressed in the given unit
77       * @param unit the unit of the value, also acts as the display unit
78       */
79      public Angle(final double valueInUnit, final Angle.Unit unit)
80      {
81          this(valueInUnit, unit, false);
82      }
83  
84      /**
85       * Return a Angle instance based on an SI value.
86       * @param si the si value
87       * @return the Angle instance based on an SI value
88       */
89      public static Angle ofSi(final double si)
90      {
91          return new Angle(si, Angle.Unit.SI, true);
92      }
93  
94      /**
95       * Instantiate a Angle quantity with an SI or base value and a display unit.
96       * @param siValue the quantity value expressed in the SI or base unit
97       * @param displayUnit the display unit to use
98       * @return the Angle instance based on an SI value with the given display unit
99       */
100     public static Angle ofSi(final double siValue, final Angle.Unit displayUnit)
101     {
102         return new Angle(siValue, displayUnit, true);
103     }
104 
105     @Override
106     public Angle instantiateSi(final double siValue, final UnitInterface<Angle> displayUnit)
107     {
108         return new Angle(siValue, (Angle.Unit) displayUnit, true);
109     }
110 
111     /**
112      * Returns a Angle representation of a textual representation of a value with a unit. The String representation that can be
113      * parsed is the double value in the unit, followed by a localized or English abbreviation of the unit. Spaces are allowed,
114      * but not required, between the value and the unit.
115      * @param text the textual representation to parse into a Angle
116      * @return the Scalar representation of the value in its unit
117      * @throws IllegalArgumentException when the text cannot be parsed
118      * @throws NullPointerException when the text argument is null
119      */
120     public static Angle valueOf(final String text)
121     {
122         return Quantity.valueOf(text, ZERO);
123     }
124 
125     /**
126      * Returns a Angle based on a value expressed in the unit.
127      * @param valueInUnit the value, expressed in the given unit
128      * @param unit the unit of the value, also acts as the display unit
129      * @return ab Angle representation of the value in its unit
130      */
131     public static Angle of(final double valueInUnit, final Angle.Unit unit)
132     {
133         return new Angle(valueInUnit, unit);
134     }
135 
136     /**
137      * Returns a Angle based on a value and the textual representation of the unit, which can be localized.
138      * @param valueInUnit the value, expressed in the unit as given by unitString
139      * @param unitString the textual representation of the unit
140      * @return the Scalar representation of the value in its unit
141      * @throws IllegalArgumentException when the unit cannot be parsed or is incorrect
142      * @throws NullPointerException when the unitString argument is null
143      */
144     public static Angle of(final double valueInUnit, final String unitString)
145     {
146         return Quantity.of(valueInUnit, unitString, ZERO);
147     }
148 
149     @Override
150     public Angle.Unit getDisplayUnit()
151     {
152         return (Angle.Unit) super.getDisplayUnit();
153     }
154 
155     /**
156      * Add an (absolute) direction to this angle, and return a direction. The unit of the return value will be the unit of this
157      * angle, and the reference of the return value will be the reference belonging to the given direction.
158      * <code>R.add(A)</code> = unit of R and reference value of A.
159      * @param direction the absolute direction to add
160      * @return the absolute direction plus this angle
161      */
162     public Direction add(final Direction direction)
163     {
164         return new Direction(new Angle(direction.si() + si(), getDisplayUnit(), true), direction.getReference());
165     }
166 
167     /**
168      * Calculate the division of Angle and Angle, which results in a Dimensionless scalar.
169      * @param v scalar
170      * @return scalar as a division of Angle and Angle
171      */
172     public Dimensionless divide(final Angle v)
173     {
174         return new Dimensionless(this.si() / v.si(), Unitless.BASE);
175     }
176 
177     /**
178      * Calculate the multiplication of Angle and Frequency, which results in a AngularVelocity scalar.
179      * @param v scalar
180      * @return scalar as a multiplication of Angle and Frequency
181      */
182     public AngularVelocity multiply(final Frequency v)
183     {
184         return new AngularVelocity(this.si() * v.si(), AngularVelocity.Unit.SI);
185     }
186 
187     /**
188      * Calculate the division of Angle and Duration, which results in a AngularVelocity scalar.
189      * @param v scalar
190      * @return scalar as a division of Angle and Duration
191      */
192     public AngularVelocity divide(final Duration v)
193     {
194         return new AngularVelocity(this.si() / v.si(), AngularVelocity.Unit.SI);
195     }
196 
197     /**
198      * Calculate the division of Angle and AngularVelocity, which results in a Duration scalar.
199      * @param v scalar
200      * @return scalar as a division of Angle and AngularVelocity
201      */
202     public Duration divide(final AngularVelocity v)
203     {
204         return new Duration(this.si() / v.si(), Duration.Unit.SI);
205     }
206 
207     /**
208      * Normalize an angle between 0 and 2 * PI.
209      * @param angle original angle.
210      * @return angle between 0 and 2 * PI.
211      */
212     public static double normalize(final double angle)
213     {
214         double normalized = angle % (2 * Math.PI);
215         if (normalized < 0.0)
216         {
217             normalized += 2 * Math.PI;
218         }
219         return normalized;
220     }
221 
222     /**
223      * Normalize an angle between 0 and 2 * PI.
224      * @param angle original angle.
225      * @return a new Angle object with angle between 0 and 2 * PI.
226      */
227     public static Angle normalize(final Angle angle)
228     {
229         return new Angle(normalize(angle.si()), Angle.Unit.rad, true);
230     }
231 
232     /******************************************************************************************************/
233     /********************************************** UNIT CLASS ********************************************/
234     /******************************************************************************************************/
235 
236     /**
237      * Angle.Unit encodes the units of angle (radians, degrees).
238      * <p>
239      * Copyright (c) 2025-2026 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
240      * See for project information <a href="https://djunits.org" target="_blank">https://djunits.org</a>. The DJUNITS project is
241      * distributed under a <a href="https://djunits.org/docs/license.html" target="_blank">three-clause BSD-style license</a>.
242      * @author Alexander Verbraeck
243      */
244     @SuppressWarnings("checkstyle:constantname")
245     public static class Unit extends AbstractUnit<Angle>
246     {
247         /** The dimensions of Angle: rad. */
248         public static final SIUnit SI_UNIT = SIUnit.of("rad");
249 
250         /** radian. */
251         public static final Angle.Unit rad =
252                 new Angle.Unit("rad", "rad", "radian", IdentityScale.SCALE, UnitSystem.SI_DERIVED, null);
253 
254         /** The SI or BASE unit. */
255         public static final Angle.Unit SI = rad;
256 
257         /** percent (non-linear, 100% is 45 degrees; 90 degrees is infinite). */
258         public static final Angle.Unit percent =
259                 new Angle.Unit("%", "%", "percent", new GradeScale(0.01), UnitSystem.OTHER, null);
260 
261         /** degree. */
262         public static final Angle.Unit deg =
263                 rad.deriveUnit("deg", "\u00b0", "degree", Math.PI / 180.0, UnitSystem.SI_ACCEPTED, null);
264 
265         /** arcminute. */
266         public static final Angle.Unit arcmin = deg.deriveUnit("arcmin", "'", "arcminute", 1.0 / 60.0, UnitSystem.OTHER, null);
267 
268         /** arcsecond. */
269         public static final Angle.Unit arcsec =
270                 deg.deriveUnit("arcsec", "\"", "arcsecond", 1.0 / 3600.0, UnitSystem.OTHER, null);
271 
272         /** grad. */
273         public static final Angle.Unit grad =
274                 rad.deriveUnit("grad", "grad", "gradian", 2.0 * Math.PI / 400.0, UnitSystem.OTHER, null);
275 
276         /** centesimal arcminute. */
277         public static final Angle.Unit cdm =
278                 grad.deriveUnit("cdm", "c'", "centesimal arcminute", 1.0 / 100.0, UnitSystem.OTHER, null);
279 
280         /** centesimal arcsecond. */
281         public static final Angle.Unit cds =
282                 grad.deriveUnit("cds", "c\"", "centesimal arcsecond", 1.0 / 10000.0, UnitSystem.OTHER, null);
283 
284         /**
285          * Create a new Angle unit.
286          * @param id the id or main abbreviation of the unit
287          * @param name the full name of the unit
288          * @param scaleFactorToBaseUnit the scale factor of the unit to convert it TO the base (SI) unit
289          * @param unitSystem the unit system such as SI or IMPERIAL
290          */
291         public Unit(final String id, final String name, final double scaleFactorToBaseUnit, final UnitSystem unitSystem)
292         {
293             super(id, name, scaleFactorToBaseUnit, unitSystem);
294         }
295 
296         /**
297          * Return a derived unit for this unit, with textual abbreviation(s) and a display abbreviation.
298          * @param textualAbbreviation the textual abbreviation of the unit, which doubles as the id
299          * @param displayAbbreviation the display abbreviation of the unit
300          * @param name the full name of the unit
301          * @param scale the scale to use to convert from this unit to the standard (e.g., SI, BASE) unit
302          * @param unitSystem unit system, e.g. SI or Imperial
303          * @param siPrefix the SI Prefix of this unit
304          */
305         public Unit(final String textualAbbreviation, final String displayAbbreviation, final String name, final Scale scale,
306                 final UnitSystem unitSystem, final SIPrefix siPrefix)
307         {
308             super(textualAbbreviation, displayAbbreviation, name, scale, unitSystem, siPrefix);
309         }
310 
311         @Override
312         public SIUnit siUnit()
313         {
314             return SI_UNIT;
315         }
316 
317         @Override
318         public Unit getBaseUnit()
319         {
320             return SI;
321         }
322 
323         @Override
324         public Angle ofSi(final double si, final UnitInterface<Angle> displayUnit)
325         {
326             return new Angle(si, (Unit) displayUnit, true);
327         }
328 
329         @Override
330         public Angle.Unit deriveUnit(final String textualAbbreviation, final String displayAbbreviation, final String name,
331                 final double scaleFactor, final UnitSystem unitSystem, final SIPrefix siPrefix)
332         {
333             if (getScale() instanceof LinearScale ls)
334             {
335                 return new Angle.Unit(textualAbbreviation, displayAbbreviation, name,
336                         new LinearScale(ls.getScaleFactorToBaseUnit() * scaleFactor), unitSystem, siPrefix);
337             }
338             throw new UnitRuntimeException("Only possible to derive a unit from a unit with a linear scale");
339         }
340 
341     }
342 
343 }