angle.go (3673B)
1 // Copyright 2014 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package s1 16 17 import ( 18 "math" 19 "strconv" 20 ) 21 22 // Angle represents a 1D angle. The internal representation is a double precision 23 // value in radians, so conversion to and from radians is exact. 24 // Conversions between E5, E6, E7, and Degrees are not always 25 // exact. For example, Degrees(3.1) is different from E6(3100000) or E7(31000000). 26 // 27 // The following conversions between degrees and radians are exact: 28 // 29 // Degree*180 == Radian*math.Pi 30 // Degree*(180/n) == Radian*(math.Pi/n) for n == 0..8 31 // 32 // These identities hold when the arguments are scaled up or down by any power 33 // of 2. Some similar identities are also true, for example, 34 // 35 // Degree*60 == Radian*(math.Pi/3) 36 // 37 // But be aware that this type of identity does not hold in general. For example, 38 // 39 // Degree*3 != Radian*(math.Pi/60) 40 // 41 // Similarly, the conversion to radians means that (Angle(x)*Degree).Degrees() 42 // does not always equal x. For example, 43 // 44 // (Angle(45*n)*Degree).Degrees() == 45*n for n == 0..8 45 // 46 // but 47 // 48 // (60*Degree).Degrees() != 60 49 // 50 // When testing for equality, you should allow for numerical errors (ApproxEqual) 51 // or convert to discrete E5/E6/E7 values first. 52 type Angle float64 53 54 // Angle units. 55 const ( 56 Radian Angle = 1 57 Degree = (math.Pi / 180) * Radian 58 59 E5 = 1e-5 * Degree 60 E6 = 1e-6 * Degree 61 E7 = 1e-7 * Degree 62 ) 63 64 // Radians returns the angle in radians. 65 func (a Angle) Radians() float64 { return float64(a) } 66 67 // Degrees returns the angle in degrees. 68 func (a Angle) Degrees() float64 { return float64(a / Degree) } 69 70 // round returns the value rounded to nearest as an int32. 71 // This does not match C++ exactly for the case of x.5. 72 func round(val float64) int32 { 73 if val < 0 { 74 return int32(val - 0.5) 75 } 76 return int32(val + 0.5) 77 } 78 79 // InfAngle returns an angle larger than any finite angle. 80 func InfAngle() Angle { 81 return Angle(math.Inf(1)) 82 } 83 84 // isInf reports whether this Angle is infinite. 85 func (a Angle) isInf() bool { 86 return math.IsInf(float64(a), 0) 87 } 88 89 // E5 returns the angle in hundred thousandths of degrees. 90 func (a Angle) E5() int32 { return round(a.Degrees() * 1e5) } 91 92 // E6 returns the angle in millionths of degrees. 93 func (a Angle) E6() int32 { return round(a.Degrees() * 1e6) } 94 95 // E7 returns the angle in ten millionths of degrees. 96 func (a Angle) E7() int32 { return round(a.Degrees() * 1e7) } 97 98 // Abs returns the absolute value of the angle. 99 func (a Angle) Abs() Angle { return Angle(math.Abs(float64(a))) } 100 101 // Normalized returns an equivalent angle in (-π, π]. 102 func (a Angle) Normalized() Angle { 103 rad := math.Remainder(float64(a), 2*math.Pi) 104 if rad <= -math.Pi { 105 rad = math.Pi 106 } 107 return Angle(rad) 108 } 109 110 func (a Angle) String() string { 111 return strconv.FormatFloat(a.Degrees(), 'f', 7, 64) // like "%.7f" 112 } 113 114 // ApproxEqual reports whether the two angles are the same up to a small tolerance. 115 func (a Angle) ApproxEqual(other Angle) bool { 116 return math.Abs(float64(a)-float64(other)) <= epsilon 117 } 118 119 // BUG(dsymonds): The major differences from the C++ version are: 120 // - no unsigned E5/E6/E7 methods