gonum.org/v1/gonum@v0.14.0/spatial/r3/box_test.go (about) 1 // Copyright ©2022 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 "testing" 9 10 "golang.org/x/exp/rand" 11 ) 12 13 func TestBoxContains(t *testing.T) { 14 rnd := rand.New(rand.NewSource(1)) 15 for i := 0; i < 200; i++ { 16 b := randomBox(rnd) 17 for j := 0; j < 10; j++ { 18 contained := b.random(rnd) 19 if !b.Contains(contained) { 20 t.Error("bounding box should contain Vec") 21 } 22 } 23 uncontained := [6]Vec{ 24 Add(b.Max, Vec{1, 0, 0}), 25 Add(b.Max, Vec{0, 1, 0}), 26 Add(b.Max, Vec{0, 0, 1}), 27 Sub(b.Min, Vec{1, 0, 0}), 28 Sub(b.Min, Vec{0, 1, 0}), 29 Sub(b.Min, Vec{0, 0, 1}), 30 } 31 for _, unc := range uncontained { 32 if b.Contains(unc) { 33 t.Error("box should not contain vec") 34 } 35 } 36 } 37 } 38 39 func TestBoxUnion(t *testing.T) { 40 rnd := rand.New(rand.NewSource(1)) 41 for i := 0; i < 200; i++ { 42 b1 := randomBox(rnd) 43 b2 := randomBox(rnd) 44 u := b1.Union(b2) 45 for j := 0; j < 10; j++ { 46 contained := b1.random(rnd) 47 if !u.Contains(contained) { 48 t.Error("union should contain b1's Vec") 49 } 50 contained = b2.random(rnd) 51 if !u.Contains(contained) { 52 t.Error("union should contain b2's Vec") 53 } 54 } 55 uncontained := [6]Vec{ 56 Add(maxElem(b1.Max, b2.Max), Vec{1, 0, 0}), 57 Add(maxElem(b1.Max, b2.Max), Vec{0, 1, 0}), 58 Add(maxElem(b1.Max, b2.Max), Vec{0, 0, 1}), 59 Sub(minElem(b1.Min, b2.Min), Vec{1, 0, 0}), 60 Sub(minElem(b1.Min, b2.Min), Vec{0, 1, 0}), 61 Sub(minElem(b1.Min, b2.Min), Vec{0, 0, 1}), 62 } 63 for _, unc := range uncontained { 64 if !b1.Contains(unc) && !b2.Contains(unc) && u.Contains(unc) { 65 t.Error("union should not contain Vec") 66 } 67 } 68 } 69 } 70 71 func TestBoxCenter(t *testing.T) { 72 const tol = 1e-11 73 rnd := rand.New(rand.NewSource(1)) 74 for i := 0; i < 300; i++ { 75 b := randomBox(rnd) 76 center := b.Center() 77 size := b.Size() 78 newBox := centeredBox(center, size) 79 if !vecApproxEqual(b.Min, newBox.Min, tol) { 80 t.Errorf("min values of box not equal. got %g, expected %g", newBox.Min, b.Min) 81 } 82 if !vecApproxEqual(b.Max, newBox.Max, tol) { 83 t.Errorf("max values of box not equal. got %g, expected %g", newBox.Max, b.Max) 84 } 85 } 86 } 87 88 func TestBoxScale(t *testing.T) { 89 const tol = 1e-11 90 rnd := rand.New(rand.NewSource(1)) 91 for i := 0; i < 300; i++ { 92 b := randomBox(rnd) 93 size := b.Size() 94 95 scaler := absElem(randomVec(rnd)) 96 scaled := b.Scale(scaler) 97 gotScaler := divElem(scaled.Size(), size) 98 if !vecApproxEqual(scaler, gotScaler, tol) { 99 t.Errorf("got scaled %g, expected %g", gotScaler, scaler) 100 } 101 center := b.Center() 102 scaledCenter := scaled.Center() 103 if !vecApproxEqual(center, scaledCenter, tol) { 104 t.Error("scale modified center") 105 } 106 } 107 } 108 109 func TestBoxVertices(t *testing.T) { 110 rnd := rand.New(rand.NewSource(1)) 111 for i := 0; i < 300; i++ { 112 b := randomBox(rnd) 113 gots := b.Vertices() 114 wants := goldenVertices(b) 115 if len(gots) != len(wants) { 116 t.Fatalf("bad length of vertices. expect 8, got %d", len(gots)) 117 } 118 for j, want := range wants { 119 got := gots[j] 120 if !vecEqual(want, got) { 121 t.Errorf("%dth vertex not equal", j) 122 } 123 } 124 } 125 } 126 127 func TestBoxEmpty(t *testing.T) { 128 rnd := rand.New(rand.NewSource(1)) 129 for i := 0; i < 300; i++ { 130 v := absElem(randomVec(rnd)) 131 b := randomBox(rnd) 132 min := b.Min 133 max := b.Max 134 if !(Box{Min: min, Max: min}).Empty() { 135 t.Error("Box{min,min} should be empty") 136 } 137 if !(Box{Min: max, Max: max}).Empty() { 138 t.Error("Box{max,max} should be empty") 139 } 140 bmm := Box{Min: min, Max: Sub(min, v)} 141 if !bmm.Empty() { 142 t.Error("Box{min,min-v} should be empty") 143 } else if bmm.Canon().Empty() { 144 t.Error("Canonical box of Box{min,min-v} is not empty") 145 } 146 bMM := Box{Min: Add(max, v), Max: max} 147 if !bMM.Empty() { 148 t.Error("Box{max+v,max} should be empty") 149 } else if bmm.Canon().Empty() { 150 t.Error("Canonical box of Box{max+v,max} is not empty") 151 } 152 } 153 } 154 155 func TestBoxCanon(t *testing.T) { 156 rnd := rand.New(rand.NewSource(1)) 157 for i := 0; i < 300; i++ { 158 b := randomBox(rnd) 159 badBox := Box{Min: b.Max, Max: b.Min} 160 canon := badBox.Canon() 161 if canon != b { 162 t.Error("swapped box canon should be equal to original box") 163 } 164 } 165 } 166 167 // randomBox returns a random valid bounding Box. 168 func randomBox(rnd *rand.Rand) Box { 169 spatialScale := randomRange(0, 2000) 170 boxScale := randomRange(0.01, 1000) 171 return centeredBox(Scale(spatialScale, randomVec(rnd)), Scale(boxScale, absElem(randomVec(rnd)))) 172 } 173 174 // Random returns a random point within the Box. 175 // used to facilitate testing 176 func (b Box) random(rnd *rand.Rand) Vec { 177 return Vec{ 178 X: randomRange(b.Min.X, b.Max.X), 179 Y: randomRange(b.Min.Y, b.Max.Y), 180 Z: randomRange(b.Min.Z, b.Max.Z), 181 } 182 } 183 184 // randomRange returns a random float64 [a,b) 185 func randomRange(a, b float64) float64 { 186 return a + (b-a)*rand.Float64() 187 } 188 189 func goldenVertices(a Box) []Vec { 190 return []Vec{ 191 0: a.Min, 192 1: {X: a.Max.X, Y: a.Min.Y, Z: a.Min.Z}, 193 2: {X: a.Max.X, Y: a.Max.Y, Z: a.Min.Z}, 194 3: {X: a.Min.X, Y: a.Max.Y, Z: a.Min.Z}, 195 4: {X: a.Min.X, Y: a.Min.Y, Z: a.Max.Z}, 196 5: {X: a.Max.X, Y: a.Min.Y, Z: a.Max.Z}, 197 6: a.Max, 198 7: {X: a.Min.X, Y: a.Max.Y, Z: a.Max.Z}, 199 } 200 }