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