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  }