github.com/gopherd/gonum@v0.0.4/spatial/r3/vector_test.go (about) 1 // Copyright ©2020 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package r3 6 7 import ( 8 "math" 9 "testing" 10 11 "github.com/gopherd/gonum/floats/scalar" 12 "github.com/gopherd/gonum/mat" 13 ) 14 15 func TestAdd(t *testing.T) { 16 for _, test := range []struct { 17 v1, v2 Vec 18 want Vec 19 }{ 20 {Vec{0, 0, 0}, Vec{0, 0, 0}, Vec{0, 0, 0}}, 21 {Vec{1, 0, 0}, Vec{0, 0, 0}, Vec{1, 0, 0}}, 22 {Vec{1, 2, 3}, Vec{4, 5, 7}, Vec{5, 7, 10}}, 23 {Vec{1, -3, 5}, Vec{1, -6, -6}, Vec{2, -9, -1}}, 24 {Vec{1, 2, 3}, Vec{-1, -2, -3}, Vec{}}, 25 } { 26 got := Add(test.v1, test.v2) 27 if got != test.want { 28 t.Errorf( 29 "error: %v + %v: got=%v, want=%v", 30 test.v1, test.v2, got, test.want, 31 ) 32 } 33 } 34 } 35 36 func TestSub(t *testing.T) { 37 for _, test := range []struct { 38 v1, v2 Vec 39 want Vec 40 }{ 41 {Vec{0, 0, 0}, Vec{0, 0, 0}, Vec{0, 0, 0}}, 42 {Vec{1, 0, 0}, Vec{0, 0, 0}, Vec{1, 0, 0}}, 43 {Vec{1, 2, 3}, Vec{4, 5, 7}, Vec{-3, -3, -4}}, 44 {Vec{1, -3, 5}, Vec{1, -6, -6}, Vec{0, 3, 11}}, 45 {Vec{1, 2, 3}, Vec{1, 2, 3}, Vec{}}, 46 } { 47 got := Sub(test.v1, test.v2) 48 if got != test.want { 49 t.Errorf( 50 "error: %v - %v: got=%v, want=%v", 51 test.v1, test.v2, got, test.want, 52 ) 53 } 54 } 55 } 56 57 func TestScale(t *testing.T) { 58 for _, test := range []struct { 59 a float64 60 v Vec 61 want Vec 62 }{ 63 {3, Vec{0, 0, 0}, Vec{0, 0, 0}}, 64 {1, Vec{1, 0, 0}, Vec{1, 0, 0}}, 65 {0, Vec{1, 0, 0}, Vec{0, 0, 0}}, 66 {3, Vec{1, 0, 0}, Vec{3, 0, 0}}, 67 {-1, Vec{1, -3, 5}, Vec{-1, 3, -5}}, 68 {2, Vec{1, -3, 5}, Vec{2, -6, 10}}, 69 {10, Vec{1, 2, 3}, Vec{10, 20, 30}}, 70 } { 71 got := Scale(test.a, test.v) 72 if got != test.want { 73 t.Errorf( 74 "error: %v * %v: got=%v, want=%v", 75 test.a, test.v, got, test.want) 76 } 77 } 78 } 79 80 func TestDot(t *testing.T) { 81 for _, test := range []struct { 82 u, v Vec 83 want float64 84 }{ 85 {Vec{1, 2, 3}, Vec{1, 2, 3}, 14}, 86 {Vec{1, 0, 0}, Vec{1, 0, 0}, 1}, 87 {Vec{1, 0, 0}, Vec{0, 1, 0}, 0}, 88 {Vec{1, 0, 0}, Vec{0, 1, 1}, 0}, 89 {Vec{1, 1, 1}, Vec{-1, -1, -1}, -3}, 90 {Vec{1, 2, 2}, Vec{-0.3, 0.4, -1.2}, -1.9}, 91 } { 92 { 93 got := Dot(test.u, test.v) 94 if got != test.want { 95 t.Errorf( 96 "error: %v · %v: got=%v, want=%v", 97 test.u, test.v, got, test.want, 98 ) 99 } 100 } 101 { 102 got := Dot(test.v, test.u) 103 if got != test.want { 104 t.Errorf( 105 "error: %v · %v: got=%v, want=%v", 106 test.v, test.u, got, test.want, 107 ) 108 } 109 } 110 } 111 } 112 113 func TestCross(t *testing.T) { 114 for _, test := range []struct { 115 v1, v2, want Vec 116 }{ 117 {Vec{1, 0, 0}, Vec{1, 0, 0}, Vec{0, 0, 0}}, 118 {Vec{1, 0, 0}, Vec{0, 1, 0}, Vec{0, 0, 1}}, 119 {Vec{0, 1, 0}, Vec{1, 0, 0}, Vec{0, 0, -1}}, 120 {Vec{1, 2, 3}, Vec{-4, 5, -6}, Vec{-27, -6, 13}}, 121 {Vec{1, 2, 3}, Vec{1, 2, 3}, Vec{}}, 122 {Vec{1, 2, 3}, Vec{2, 3, 4}, Vec{-1, 2, -1}}, 123 } { 124 got := Cross(test.v1, test.v2) 125 if got != test.want { 126 t.Errorf( 127 "error: %v × %v = %v, want %v", 128 test.v1, test.v2, got, test.want, 129 ) 130 } 131 } 132 } 133 134 func TestNorm(t *testing.T) { 135 for _, test := range []struct { 136 v Vec 137 want float64 138 }{ 139 {Vec{0, 0, 0}, 0}, 140 {Vec{0, 1, 0}, 1}, 141 {Vec{3, -4, 12}, 13}, 142 {Vec{1, 1e-16, 1e-32}, 1}, 143 {Vec{-0, 4.3145006366056343748277397783556100978621924913975e-196, 4.3145006366056343748277397783556100978621924913975e-196}, 6.101625315155041e-196}, 144 } { 145 if got, want := Norm(test.v), test.want; got != want { 146 t.Errorf("|%v| = %v, want %v", test.v, got, want) 147 } 148 } 149 } 150 151 func TestNorm2(t *testing.T) { 152 for _, test := range []struct { 153 v Vec 154 want float64 155 }{ 156 {Vec{0, 0, 0}, 0}, 157 {Vec{0, 1, 0}, 1}, 158 {Vec{1, 1, 1}, 3}, 159 {Vec{1, 2, 3}, 14}, 160 {Vec{3, -4, 12}, 169}, 161 {Vec{1, 1e-16, 1e-32}, 1}, 162 // This will underflow and return zero. 163 {Vec{-0, 4.3145006366056343748277397783556100978621924913975e-196, 4.3145006366056343748277397783556100978621924913975e-196}, 0}, 164 } { 165 if got, want := Norm2(test.v), test.want; got != want { 166 t.Errorf("|%v|^2 = %v, want %v", test.v, got, want) 167 } 168 } 169 } 170 171 func TestUnit(t *testing.T) { 172 for _, test := range []struct { 173 v, want Vec 174 }{ 175 {Vec{}, Vec{math.NaN(), math.NaN(), math.NaN()}}, 176 {Vec{1, 0, 0}, Vec{1, 0, 0}}, 177 {Vec{0, 1, 0}, Vec{0, 1, 0}}, 178 {Vec{0, 0, 1}, Vec{0, 0, 1}}, 179 {Vec{1, 1, 1}, Vec{1. / math.Sqrt(3), 1. / math.Sqrt(3), 1. / math.Sqrt(3)}}, 180 {Vec{1, 1e-16, 1e-32}, Vec{1, 1e-16, 1e-32}}, 181 } { 182 got := Unit(test.v) 183 if !vecEqual(got, test.want) { 184 t.Errorf( 185 "Normalize(%v) = %v, want %v", 186 test.v, got, test.want, 187 ) 188 } 189 if test.v == (Vec{}) { 190 return 191 } 192 if n, want := Norm(got), 1.0; n != want { 193 t.Errorf("|%v| = %v, want 1", got, n) 194 } 195 } 196 } 197 198 func TestCos(t *testing.T) { 199 for _, test := range []struct { 200 v1, v2 Vec 201 want float64 202 }{ 203 {Vec{1, 1, 1}, Vec{1, 1, 1}, 1}, 204 {Vec{1, 1, 1}, Vec{-1, -1, -1}, -1}, 205 {Vec{1, 1, 1}, Vec{1, -1, 1}, 1.0 / 3}, 206 {Vec{1, 0, 0}, Vec{1, 0, 0}, 1}, 207 {Vec{1, 0, 0}, Vec{0, 1, 0}, 0}, 208 {Vec{1, 0, 0}, Vec{0, 1, 1}, 0}, 209 {Vec{1, 0, 0}, Vec{-1, 0, 0}, -1}, 210 } { 211 tol := 1e-14 212 got := Cos(test.v1, test.v2) 213 if !scalar.EqualWithinAbs(got, test.want, tol) { 214 t.Errorf("cos(%v, %v)= %v, want %v", 215 test.v1, test.v2, got, test.want, 216 ) 217 } 218 } 219 } 220 221 func TestRotate(t *testing.T) { 222 const tol = 1e-14 223 for _, test := range []struct { 224 v, axis Vec 225 alpha float64 226 want Vec 227 }{ 228 {Vec{1, 0, 0}, Vec{1, 0, 0}, math.Pi / 2, Vec{1, 0, 0}}, 229 {Vec{1, 0, 0}, Vec{1, 0, 0}, 0, Vec{1, 0, 0}}, 230 {Vec{1, 0, 0}, Vec{1, 0, 0}, 2 * math.Pi, Vec{1, 0, 0}}, 231 {Vec{1, 0, 0}, Vec{0, 0, 0}, math.Pi / 2, Vec{math.NaN(), math.NaN(), math.NaN()}}, 232 {Vec{1, 0, 0}, Vec{0, 1, 0}, math.Pi / 2, Vec{0, 0, -1}}, 233 {Vec{1, 0, 0}, Vec{0, 1, 0}, math.Pi, Vec{-1, 0, 0}}, 234 {Vec{2, 0, 0}, Vec{0, 1, 0}, math.Pi, Vec{-2, 0, 0}}, 235 {Vec{1, 2, 3}, Vec{1, 1, 1}, 2. / 3. * math.Pi, Vec{3, 1, 2}}, 236 } { 237 got := Rotate(test.v, test.alpha, test.axis) 238 if !vecApproxEqual(got, test.want, tol) { 239 t.Errorf( 240 "quat rotate(%v, %v, %v)= %v, want=%v", 241 test.v, test.alpha, test.axis, got, test.want, 242 ) 243 } 244 245 var gotv mat.VecDense 246 gotv.MulVec(NewRotation(test.alpha, test.axis).Mat(), vecDense(test.v)) 247 got = vec(gotv) 248 if !vecApproxEqual(got, test.want, tol) { 249 t.Errorf( 250 "matrix rotate(%v, %v, %v)= %v, want=%v", 251 test.v, test.alpha, test.axis, got, test.want, 252 ) 253 } 254 } 255 } 256 257 func vecDense(v Vec) *mat.VecDense { 258 return mat.NewVecDense(3, []float64{v.X, v.Y, v.Z}) 259 } 260 261 func vec(v mat.VecDense) Vec { 262 if v.Len() != 3 { 263 panic(mat.ErrShape) 264 } 265 return Vec{v.AtVec(0), v.AtVec(1), v.AtVec(2)} 266 } 267 268 func vecIsNaN(v Vec) bool { 269 return math.IsNaN(v.X) && math.IsNaN(v.Y) && math.IsNaN(v.Z) 270 } 271 272 func vecIsNaNAny(v Vec) bool { 273 return math.IsNaN(v.X) || math.IsNaN(v.Y) || math.IsNaN(v.Z) 274 } 275 276 func vecEqual(a, b Vec) bool { 277 if vecIsNaNAny(a) || vecIsNaNAny(b) { 278 return vecIsNaN(a) && vecIsNaN(b) 279 } 280 return a == b 281 } 282 283 func vecApproxEqual(a, b Vec, tol float64) bool { 284 if vecIsNaNAny(a) || vecIsNaNAny(b) { 285 return vecIsNaN(a) && vecIsNaN(b) 286 } 287 return scalar.EqualWithinAbs(a.X, b.X, tol) && 288 scalar.EqualWithinAbs(a.Y, b.Y, tol) && 289 scalar.EqualWithinAbs(a.Z, b.Z, tol) 290 }