mirror of
https://github.com/gonum/gonum.git
synced 2025-10-06 15:47:01 +08:00
spatial/r3: add Box
This commit is contained in:
200
spatial/r3/box_test.go
Normal file
200
spatial/r3/box_test.go
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright ©2022 The Gonum Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package r3
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/exp/rand"
|
||||
)
|
||||
|
||||
func TestBoxContains(t *testing.T) {
|
||||
rnd := rand.New(rand.NewSource(1))
|
||||
for i := 0; i < 200; i++ {
|
||||
b := randomBox(rnd)
|
||||
for j := 0; j < 10; j++ {
|
||||
contained := b.random(rnd)
|
||||
if !b.Contains(contained) {
|
||||
t.Error("bounding box should contain Vec")
|
||||
}
|
||||
}
|
||||
uncontained := [6]Vec{
|
||||
Add(b.Max, Vec{1, 0, 0}),
|
||||
Add(b.Max, Vec{0, 1, 0}),
|
||||
Add(b.Max, Vec{0, 0, 1}),
|
||||
Sub(b.Min, Vec{1, 0, 0}),
|
||||
Sub(b.Min, Vec{0, 1, 0}),
|
||||
Sub(b.Min, Vec{0, 0, 1}),
|
||||
}
|
||||
for _, unc := range uncontained {
|
||||
if b.Contains(unc) {
|
||||
t.Error("box should not contain vec")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoxUnion(t *testing.T) {
|
||||
rnd := rand.New(rand.NewSource(1))
|
||||
for i := 0; i < 200; i++ {
|
||||
b1 := randomBox(rnd)
|
||||
b2 := randomBox(rnd)
|
||||
u := b1.Union(b2)
|
||||
for j := 0; j < 10; j++ {
|
||||
contained := b1.random(rnd)
|
||||
if !u.Contains(contained) {
|
||||
t.Error("union should contain b1's Vec")
|
||||
}
|
||||
contained = b2.random(rnd)
|
||||
if !u.Contains(contained) {
|
||||
t.Error("union should contain b2's Vec")
|
||||
}
|
||||
}
|
||||
uncontained := [6]Vec{
|
||||
Add(maxElem(b1.Max, b2.Max), Vec{1, 0, 0}),
|
||||
Add(maxElem(b1.Max, b2.Max), Vec{0, 1, 0}),
|
||||
Add(maxElem(b1.Max, b2.Max), Vec{0, 0, 1}),
|
||||
Sub(minElem(b1.Min, b2.Min), Vec{1, 0, 0}),
|
||||
Sub(minElem(b1.Min, b2.Min), Vec{0, 1, 0}),
|
||||
Sub(minElem(b1.Min, b2.Min), Vec{0, 0, 1}),
|
||||
}
|
||||
for _, unc := range uncontained {
|
||||
if !b1.Contains(unc) && !b2.Contains(unc) && u.Contains(unc) {
|
||||
t.Error("union should not contain Vec")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoxCenter(t *testing.T) {
|
||||
const tol = 1e-11
|
||||
rnd := rand.New(rand.NewSource(1))
|
||||
for i := 0; i < 300; i++ {
|
||||
b := randomBox(rnd)
|
||||
center := b.Center()
|
||||
size := b.Size()
|
||||
newBox := centeredBox(center, size)
|
||||
if !vecApproxEqual(b.Min, newBox.Min, tol) {
|
||||
t.Errorf("min values of box not equal. got %g, expected %g", newBox.Min, b.Min)
|
||||
}
|
||||
if !vecApproxEqual(b.Max, newBox.Max, tol) {
|
||||
t.Errorf("max values of box not equal. got %g, expected %g", newBox.Max, b.Max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoxScale(t *testing.T) {
|
||||
const tol = 1e-11
|
||||
rnd := rand.New(rand.NewSource(1))
|
||||
for i := 0; i < 300; i++ {
|
||||
b := randomBox(rnd)
|
||||
size := b.Size()
|
||||
|
||||
scaler := absElem(randomVec(rnd))
|
||||
scaled := b.Scale(scaler)
|
||||
gotScaler := divElem(scaled.Size(), size)
|
||||
if !vecApproxEqual(scaler, gotScaler, tol) {
|
||||
t.Errorf("got scaled %g, expected %g", gotScaler, scaler)
|
||||
}
|
||||
center := b.Center()
|
||||
scaledCenter := scaled.Center()
|
||||
if !vecApproxEqual(center, scaledCenter, tol) {
|
||||
t.Error("scale modified center")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoxVertices(t *testing.T) {
|
||||
rnd := rand.New(rand.NewSource(1))
|
||||
for i := 0; i < 300; i++ {
|
||||
b := randomBox(rnd)
|
||||
gots := b.Vertices()
|
||||
wants := goldenVertices(b)
|
||||
if len(gots) != len(wants) {
|
||||
t.Fatalf("bad length of vertices. expect 8, got %d", len(gots))
|
||||
}
|
||||
for j, want := range wants {
|
||||
got := gots[j]
|
||||
if !vecEqual(want, got) {
|
||||
t.Errorf("%dth vertex not equal", j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoxEmpty(t *testing.T) {
|
||||
rnd := rand.New(rand.NewSource(1))
|
||||
for i := 0; i < 300; i++ {
|
||||
v := absElem(randomVec(rnd))
|
||||
b := randomBox(rnd)
|
||||
min := b.Min
|
||||
max := b.Max
|
||||
if !(Box{Min: min, Max: min}).Empty() {
|
||||
t.Error("Box{min,min} should be empty")
|
||||
}
|
||||
if !(Box{Min: max, Max: max}).Empty() {
|
||||
t.Error("Box{max,max} should be empty")
|
||||
}
|
||||
bmm := Box{Min: min, Max: Sub(min, v)}
|
||||
if !bmm.Empty() {
|
||||
t.Error("Box{min,min-v} should be empty")
|
||||
} else if bmm.Canon().Empty() {
|
||||
t.Error("Canonical box of Box{min,min-v} is not empty")
|
||||
}
|
||||
bMM := Box{Min: Add(max, v), Max: max}
|
||||
if !bMM.Empty() {
|
||||
t.Error("Box{max+v,max} should be empty")
|
||||
} else if bmm.Canon().Empty() {
|
||||
t.Error("Canonical box of Box{max+v,max} is not empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoxCanon(t *testing.T) {
|
||||
rnd := rand.New(rand.NewSource(1))
|
||||
for i := 0; i < 300; i++ {
|
||||
b := randomBox(rnd)
|
||||
badBox := Box{Min: b.Max, Max: b.Min}
|
||||
canon := badBox.Canon()
|
||||
if canon != b {
|
||||
t.Error("swapped box canon should be equal to original box")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// randomBox returns a random valid bounding Box.
|
||||
func randomBox(rnd *rand.Rand) Box {
|
||||
spatialScale := randomRange(0, 2000)
|
||||
boxScale := randomRange(0.01, 1000)
|
||||
return centeredBox(Scale(spatialScale, randomVec(rnd)), Scale(boxScale, absElem(randomVec(rnd))))
|
||||
}
|
||||
|
||||
// Random returns a random point within the Box.
|
||||
// used to facilitate testing
|
||||
func (b Box) random(rnd *rand.Rand) Vec {
|
||||
return Vec{
|
||||
X: randomRange(b.Min.X, b.Max.X),
|
||||
Y: randomRange(b.Min.Y, b.Max.Y),
|
||||
Z: randomRange(b.Min.Z, b.Max.Z),
|
||||
}
|
||||
}
|
||||
|
||||
// randomRange returns a random float64 [a,b)
|
||||
func randomRange(a, b float64) float64 {
|
||||
return a + (b-a)*rand.Float64()
|
||||
}
|
||||
|
||||
func goldenVertices(a Box) []Vec {
|
||||
return []Vec{
|
||||
0: a.Min,
|
||||
1: {X: a.Max.X, Y: a.Min.Y, Z: a.Min.Z},
|
||||
2: {X: a.Max.X, Y: a.Max.Y, Z: a.Min.Z},
|
||||
3: {X: a.Min.X, Y: a.Max.Y, Z: a.Min.Z},
|
||||
4: {X: a.Min.X, Y: a.Min.Y, Z: a.Max.Z},
|
||||
5: {X: a.Max.X, Y: a.Min.Y, Z: a.Max.Z},
|
||||
6: a.Max,
|
||||
7: {X: a.Min.X, Y: a.Max.Y, Z: a.Max.Z},
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user