Angle.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.numbers.angle;

  18. import java.util.function.DoubleSupplier;
  19. import java.util.function.DoubleUnaryOperator;

  20. /**
  21.  * Represents the <a href="https://en.wikipedia.org/wiki/Angle">angle</a> concept.
  22.  */
  23. public abstract class Angle implements DoubleSupplier {
  24.     /** 2&pi;. */
  25.     public static final double TWO_PI = 2 * Math.PI;
  26.     /** &pi;/2. */
  27.     public static final double PI_OVER_TWO = 0.5 * Math.PI;
  28.     /** Turns to degrees conversion factor. */
  29.     private static final double TURN_TO_DEG = 360d;
  30.     /** Radians to degrees conversion factor. */
  31.     private static final double RAD_TO_DEG = 180.0 / Math.PI;
  32.     /** Degrees to radians conversion factor. */
  33.     private static final double DEG_TO_RAD = Math.PI / 180.0;

  34.     /** Value (unit depends on concrete instance). */
  35.     private final double value;

  36.     /**
  37.      * @param value Value in turns.
  38.      */
  39.     Angle(final double value) {
  40.         this.value = value;
  41.     }

  42.     /** @return the value. */
  43.     @Override
  44.     public double getAsDouble() {
  45.         return value;
  46.     }

  47.     /** {@inheritDoc} */
  48.     @Override
  49.     public int hashCode() {
  50.         return Double.hashCode(value);
  51.     }

  52.     /**
  53.      * Test for equality with another object.
  54.      * Objects are considered to be equal if their concrete types are
  55.      * equal and their values are exactly the same (or both are {@code Double.NaN}).
  56.      *
  57.      * @param other Object to test for equality with this instance.
  58.      * @return {@code true} if the objects are equal, {@code false} if
  59.      * {@code other} is {@code null}, not of the same type as this instance,
  60.      * or not equal to this instance.
  61.      */
  62.     @Override
  63.     public boolean equals(final Object other) {
  64.         return other != null &&
  65.                 getClass().equals(other.getClass()) &&
  66.                 Double.doubleToLongBits(value) == Double.doubleToLongBits(((Angle) other).value);
  67.     }

  68.     /**
  69.      * Convert to a {@link Turn}.
  70.      *
  71.      * @return the angle in <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>.
  72.      */
  73.     public abstract Turn toTurn();

  74.     /**
  75.      * Convert to a {@link Rad}.
  76.      *
  77.      * @return the angle in <a href="https://en.wikipedia.org/wiki/Radian">radians</a>.
  78.      */
  79.     public abstract Rad toRad();

  80.     /**
  81.      * Convert to a {@link Deg}.
  82.      *
  83.      * @return the angle in <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>.
  84.      */
  85.     public abstract Deg toDeg();

  86.     /**
  87.      * Unit: <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>.
  88.      */
  89.     public static final class Turn extends Angle {
  90.         /** Zero. */
  91.         public static final Turn ZERO = of(0d);
  92.         /** Normalizing operator (result will be within the {@code [0, 1[} interval). */
  93.         public static final DoubleUnaryOperator WITHIN_0_AND_1 = normalizer(0d);

  94.         /**
  95.          * Create an instance.
  96.          *
  97.          * @param angle (in turns).
  98.          */
  99.         private Turn(final double angle) {
  100.             super(angle);
  101.         }

  102.         /**
  103.          * Create an instance.
  104.          *
  105.          * @param angle (in turns).
  106.          * @return a new instance.
  107.          */
  108.         public static Turn of(final double angle) {
  109.             return new Turn(angle);
  110.         }

  111.         /** {@inheritDoc} */
  112.         @Override
  113.         public Turn toTurn() {
  114.             return this;
  115.         }

  116.         /** {@inheritDoc} */
  117.         @Override
  118.         public Rad toRad() {
  119.             return Rad.of(getAsDouble() * TWO_PI);
  120.         }

  121.         /** {@inheritDoc} */
  122.         @Override
  123.         public Deg toDeg() {
  124.             return Deg.of(getAsDouble() * TURN_TO_DEG);
  125.         }

  126.         /**
  127.          * Creates an operator for normalizing/reducing an angle.
  128.          * The output will be within the {@code [lo, lo + 1[} interval.
  129.          *
  130.          * @param lo Lower bound of the normalized interval.
  131.          * @return the normalization operator.
  132.          */
  133.         public static DoubleUnaryOperator normalizer(final double lo) {
  134.             return new Normalizer(lo, 1d);
  135.         }
  136.     }

  137.     /**
  138.      * Unit: <a href="https://en.wikipedia.org/wiki/Radian">radians</a>.
  139.      */
  140.     public static final class Rad extends Angle {
  141.         /** Zero. */
  142.         public static final Rad ZERO = of(0d);
  143.         /** &pi;. */
  144.         public static final Rad PI = of(Math.PI);
  145.         /** 2&pi;. */
  146.         public static final Rad TWO_PI = of(Angle.TWO_PI);
  147.         /** Normalizing operator (result will be within the <code>[0, 2&pi;[</code> interval). */
  148.         public static final DoubleUnaryOperator WITHIN_0_AND_2PI = normalizer(0d);
  149.         /** Normalizing operator (result will be within the <code>[-&pi;, &pi;[</code> interval). */
  150.         public static final DoubleUnaryOperator WITHIN_MINUS_PI_AND_PI = normalizer(-Math.PI);

  151.         /**
  152.          * Create an instance.
  153.          *
  154.          * @param angle (in radians).
  155.          */
  156.         private Rad(final double angle) {
  157.             super(angle);
  158.         }

  159.         /**
  160.          * Create an instance.
  161.          *
  162.          * @param angle (in radians).
  163.          * @return a new instance.
  164.          */
  165.         public static Rad of(final double angle) {
  166.             return new Rad(angle);
  167.         }

  168.         /** {@inheritDoc} */
  169.         @Override
  170.         public Turn toTurn() {
  171.             return Turn.of(getAsDouble() / Angle.TWO_PI);
  172.         }

  173.         /** {@inheritDoc} */
  174.         @Override
  175.         public Rad toRad() {
  176.             return this;
  177.         }

  178.         /** {@inheritDoc} */
  179.         @Override
  180.         public Deg toDeg() {
  181.             return Deg.of(getAsDouble() * RAD_TO_DEG);
  182.         }

  183.         /**
  184.          * Creates an operator for normalizing/reducing an angle.
  185.          * The output will be within the <code> [lo, lo + 2&pi;[</code> interval.
  186.          *
  187.          * @param lo Lower bound of the normalized interval.
  188.          * @return the normalization operator.
  189.          */
  190.         public static DoubleUnaryOperator normalizer(final double lo) {
  191.             return new Normalizer(lo, Angle.TWO_PI);
  192.         }
  193.     }

  194.     /**
  195.      * Unit: <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>.
  196.      */
  197.     public static final class Deg extends Angle {
  198.         /** Zero. */
  199.         public static final Deg ZERO = of(0d);
  200.         /** Normalizing operator (result will be within the {@code [0, 360[} interval). */
  201.         public static final DoubleUnaryOperator WITHIN_0_AND_360 = normalizer(0d);

  202.         /**
  203.          * Create an instance.
  204.          *
  205.          * @param angle (in degrees).
  206.          */
  207.         private Deg(final double angle) {
  208.             super(angle);
  209.         }

  210.         /**
  211.          * Create an instance.
  212.          *
  213.          * @param angle (in degrees).
  214.          * @return a new instance.
  215.          */
  216.         public static Deg of(final double angle) {
  217.             return new Deg(angle);
  218.         }

  219.         /** {@inheritDoc} */
  220.         @Override
  221.         public Turn toTurn() {
  222.             return Turn.of(getAsDouble() / TURN_TO_DEG);
  223.         }

  224.         /** {@inheritDoc} */
  225.         @Override
  226.         public Rad toRad() {
  227.             return Rad.of(getAsDouble() * DEG_TO_RAD);
  228.         }

  229.         /** {@inheritDoc} */
  230.         @Override
  231.         public Deg toDeg() {
  232.             return this;
  233.         }

  234.         /**
  235.          * Creates an operator for normalizing/reducing an angle.
  236.          * The output will be within the {@code [c, c + 360[} interval.
  237.          *
  238.          * @param lo Lower bound of the normalized interval.
  239.          * @return the normalization operator.
  240.          */
  241.         public static DoubleUnaryOperator normalizer(final double lo) {
  242.             return new Normalizer(lo, TURN_TO_DEG);
  243.         }
  244.     }

  245.     /**
  246.      * Normalizes an angle around a center value.
  247.      */
  248.     private static final class Normalizer implements DoubleUnaryOperator {
  249.         /** Lower bound. */
  250.         private final double lo;
  251.         /** Upper bound. */
  252.         private final double hi;
  253.         /** Period. */
  254.         private final double period;
  255.         /** Normalizer. */
  256.         private final Reduce reduce;

  257.         /**
  258.          * Note: It is assumed that both arguments have the same unit.
  259.          *
  260.          * @param lo Lower bound of the desired interval.
  261.          * @param period Circonference of the circle.
  262.          */
  263.         Normalizer(final double lo,
  264.                    final double period) {
  265.             this.period = period;
  266.             this.lo = lo;
  267.             this.hi = lo + period;
  268.             reduce = new Reduce(lo, period);
  269.         }

  270.         /**
  271.          * @param a Angle.
  272.          * @return {@code = a - k} where {@code k} is an integer that satisfies
  273.          * {@code lo <= a - k < lo + period}.
  274.          */
  275.         @Override
  276.         public double applyAsDouble(final double a) {
  277.             if (lo <= a &&
  278.                 a < hi) {
  279.                 // Already within the main interval.
  280.                 return a;
  281.             }

  282.             final double normalized = reduce.applyAsDouble(a) + lo;
  283.             return normalized < hi ?
  284.                 normalized :
  285.                 // If value is too small to be representable compared to the
  286.                 // floor expression above (i.e. value + x = x), then we may
  287.                 // end up with a number exactly equal to the upper bound.
  288.                 // In that case, subtract one period from the normalized value
  289.                 // so that the result is strictly less than the upper bound. (We also
  290.                 // want to ensure that we do not return anything less than the lower bound.)
  291.                 Math.max(lo, normalized - period);
  292.         }
  293.     }
  294. }