001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.numbers.angle; 018 019/** 020 * Represents the <a href="https://en.wikipedia.org/wiki/Angle">angle</a> concept. 021 */ 022public final class PlaneAngle { 023 /** Zero. */ 024 public static final PlaneAngle ZERO = new PlaneAngle(0); 025 /** Half-turn (aka π radians). */ 026 public static final PlaneAngle PI = new PlaneAngle(0.5); 027 /** Conversion factor. */ 028 private static final double HALF_TURN = 0.5; 029 /** Conversion factor. */ 030 private static final double TO_RADIANS = PlaneAngleRadians.TWO_PI; 031 /** Conversion factor. */ 032 private static final double FROM_RADIANS = 1d / TO_RADIANS; 033 /** Conversion factor. */ 034 private static final double TO_DEGREES = 360; 035 /** Conversion factor. */ 036 private static final double FROM_DEGREES = 1d / TO_DEGREES; 037 /** Value (in turns). */ 038 private final double value; 039 040 /** 041 * @param value Value in turns. 042 */ 043 private PlaneAngle(double value) { 044 this.value = value; 045 } 046 047 /** 048 * @param angle (in <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>). 049 * @return a new intance. 050 */ 051 public static PlaneAngle ofTurns(double angle) { 052 return new PlaneAngle(angle); 053 } 054 055 /** 056 * @param angle (in <a href="https://en.wikipedia.org/wiki/Radian">radians</a>). 057 * @return a new intance. 058 */ 059 public static PlaneAngle ofRadians(double angle) { 060 return new PlaneAngle(angle * FROM_RADIANS); 061 } 062 063 /** 064 * @param angle (in <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>). 065 * @return a new intance. 066 */ 067 public static PlaneAngle ofDegrees(double angle) { 068 return new PlaneAngle(angle * FROM_DEGREES); 069 } 070 071 /** 072 * @return the value in <a href="https://en.wikipedia.org/wiki/Turn_%28geometry%29">turns</a>. 073 */ 074 public double toTurns() { 075 return value; 076 } 077 078 /** 079 * @return the value in <a href="https://en.wikipedia.org/wiki/Radian">radians</a>. 080 */ 081 public double toRadians() { 082 return value * TO_RADIANS; 083 } 084 085 /** 086 * @return the value in <a href="https://en.wikipedia.org/wiki/Degree_%28angle%29">degrees</a>. 087 */ 088 public double toDegrees() { 089 return value * TO_DEGREES; 090 } 091 092 /** 093 * Normalize an angle in an interval of size 1 turn around a 094 * center value. 095 * 096 * @param center Center of the desired interval for the result. 097 * @return {@code a - k} with integer {@code k} such that 098 * {@code center - 0.5 <= a - k < center + 0.5} (in turns). 099 */ 100 public PlaneAngle normalize(PlaneAngle center) { 101 final double lowerBound = center.value - HALF_TURN; 102 final double upperBound = center.value + HALF_TURN; 103 104 final double normalized = value - Math.floor(value - lowerBound); 105 106 return normalized < upperBound ? 107 new PlaneAngle(normalized) : 108 // If value is too small to be representable compared to the 109 // floor expression above (ie, if value + x = x), then we may 110 // end up with a number exactly equal to the upper bound here. 111 // In that case, subtract one from the normalized value so that 112 // we can fulfill the contract of only returning results strictly 113 // less than the upper bound. 114 new PlaneAngle(normalized - 1); 115 } 116 117 /** 118 * Test for equality with another object. 119 * Objects are considered to be equal if the two values are exactly the 120 * same, or both are {@code Double.NaN}. 121 * 122 * @param other Object to test for equality with this instance. 123 * @return {@code true} if the objects are equal, {@code false} if 124 * {@code other} is {@code null}, not an instance of {@code PlaneAngle}, 125 * or not equal to this instance. 126 */ 127 @Override 128 public boolean equals(Object other) { 129 if (this == other) { 130 return true; 131 } 132 if (other instanceof PlaneAngle) { 133 return Double.doubleToLongBits(value) == 134 Double.doubleToLongBits(((PlaneAngle) other).value); 135 } 136 return false; 137 } 138 139 /** {@inheritDoc} */ 140 @Override 141 public int hashCode() { 142 return Double.hashCode(value); 143 } 144}