vector.go (4603B)
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 r3 16 17 import ( 18 "fmt" 19 "math" 20 21 "github.com/golang/geo/s1" 22 ) 23 24 // Vector represents a point in ℝ³. 25 type Vector struct { 26 X, Y, Z float64 27 } 28 29 // ApproxEqual reports whether v and ov are equal within a small epsilon. 30 func (v Vector) ApproxEqual(ov Vector) bool { 31 const epsilon = 1e-16 32 return math.Abs(v.X-ov.X) < epsilon && math.Abs(v.Y-ov.Y) < epsilon && math.Abs(v.Z-ov.Z) < epsilon 33 } 34 35 func (v Vector) String() string { return fmt.Sprintf("(%0.24f, %0.24f, %0.24f)", v.X, v.Y, v.Z) } 36 37 // Norm returns the vector's norm. 38 func (v Vector) Norm() float64 { return math.Sqrt(v.Dot(v)) } 39 40 // Norm2 returns the square of the norm. 41 func (v Vector) Norm2() float64 { return v.Dot(v) } 42 43 // Normalize returns a unit vector in the same direction as v. 44 func (v Vector) Normalize() Vector { 45 n2 := v.Norm2() 46 if n2 == 0 { 47 return Vector{0, 0, 0} 48 } 49 return v.Mul(1 / math.Sqrt(n2)) 50 } 51 52 // IsUnit returns whether this vector is of approximately unit length. 53 func (v Vector) IsUnit() bool { 54 const epsilon = 5e-14 55 return math.Abs(v.Norm2()-1) <= epsilon 56 } 57 58 // Abs returns the vector with nonnegative components. 59 func (v Vector) Abs() Vector { return Vector{math.Abs(v.X), math.Abs(v.Y), math.Abs(v.Z)} } 60 61 // Add returns the standard vector sum of v and ov. 62 func (v Vector) Add(ov Vector) Vector { return Vector{v.X + ov.X, v.Y + ov.Y, v.Z + ov.Z} } 63 64 // Sub returns the standard vector difference of v and ov. 65 func (v Vector) Sub(ov Vector) Vector { return Vector{v.X - ov.X, v.Y - ov.Y, v.Z - ov.Z} } 66 67 // Mul returns the standard scalar product of v and m. 68 func (v Vector) Mul(m float64) Vector { return Vector{m * v.X, m * v.Y, m * v.Z} } 69 70 // Dot returns the standard dot product of v and ov. 71 func (v Vector) Dot(ov Vector) float64 { return v.X*ov.X + v.Y*ov.Y + v.Z*ov.Z } 72 73 // Cross returns the standard cross product of v and ov. 74 func (v Vector) Cross(ov Vector) Vector { 75 return Vector{ 76 v.Y*ov.Z - v.Z*ov.Y, 77 v.Z*ov.X - v.X*ov.Z, 78 v.X*ov.Y - v.Y*ov.X, 79 } 80 } 81 82 // Distance returns the Euclidean distance between v and ov. 83 func (v Vector) Distance(ov Vector) float64 { return v.Sub(ov).Norm() } 84 85 // Angle returns the angle between v and ov. 86 func (v Vector) Angle(ov Vector) s1.Angle { 87 return s1.Angle(math.Atan2(v.Cross(ov).Norm(), v.Dot(ov))) * s1.Radian 88 } 89 90 // Axis enumerates the 3 axes of ℝ³. 91 type Axis int 92 93 // The three axes of ℝ³. 94 const ( 95 XAxis Axis = iota 96 YAxis 97 ZAxis 98 ) 99 100 // Ortho returns a unit vector that is orthogonal to v. 101 // Ortho(-v) = -Ortho(v) for all v. 102 func (v Vector) Ortho() Vector { 103 ov := Vector{0.012, 0.0053, 0.00457} 104 switch v.LargestComponent() { 105 case XAxis: 106 ov.Z = 1 107 case YAxis: 108 ov.X = 1 109 default: 110 ov.Y = 1 111 } 112 return v.Cross(ov).Normalize() 113 } 114 115 // LargestComponent returns the axis that represents the largest component in this vector. 116 func (v Vector) LargestComponent() Axis { 117 t := v.Abs() 118 119 if t.X > t.Y { 120 if t.X > t.Z { 121 return XAxis 122 } 123 return ZAxis 124 } 125 if t.Y > t.Z { 126 return YAxis 127 } 128 return ZAxis 129 } 130 131 // SmallestComponent returns the axis that represents the smallest component in this vector. 132 func (v Vector) SmallestComponent() Axis { 133 t := v.Abs() 134 135 if t.X < t.Y { 136 if t.X < t.Z { 137 return XAxis 138 } 139 return ZAxis 140 } 141 if t.Y < t.Z { 142 return YAxis 143 } 144 return ZAxis 145 } 146 147 // Cmp compares v and ov lexicographically and returns: 148 // 149 // -1 if v < ov 150 // 0 if v == ov 151 // +1 if v > ov 152 // 153 // This method is based on C++'s std::lexicographical_compare. Two entities 154 // are compared element by element with the given operator. The first mismatch 155 // defines which is less (or greater) than the other. If both have equivalent 156 // values they are lexicographically equal. 157 func (v Vector) Cmp(ov Vector) int { 158 if v.X < ov.X { 159 return -1 160 } 161 if v.X > ov.X { 162 return 1 163 } 164 165 // First elements were the same, try the next. 166 if v.Y < ov.Y { 167 return -1 168 } 169 if v.Y > ov.Y { 170 return 1 171 } 172 173 // Second elements were the same return the final compare. 174 if v.Z < ov.Z { 175 return -1 176 } 177 if v.Z > ov.Z { 178 return 1 179 } 180 181 // Both are equal 182 return 0 183 }