AABB and Sphere Bounds with new BVH tree implement with go 1.18 generic
This commit is contained in:
3
go.mod
3
go.mod
@ -1,8 +1,9 @@
|
|||||||
module github.com/Tnze/go-mc
|
module github.com/Tnze/go-mc
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/iancoleman/strcase v0.1.3
|
github.com/iancoleman/strcase v0.1.3
|
||||||
|
golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7
|
||||||
)
|
)
|
2
go.sum
2
go.sum
@ -2,3 +2,5 @@ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
|||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CTdBw=
|
github.com/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CTdBw=
|
||||||
github.com/iancoleman/strcase v0.1.3/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
github.com/iancoleman/strcase v0.1.3/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||||
|
golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7 h1:jynE66seADJbyWMUdeOyVTvPtBZt7L6LJHupGwxPZRM=
|
||||||
|
golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||||
|
59
server/internal/bvh/bound.go
Normal file
59
server/internal/bvh/bound.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package bvh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AABB[I constraints.Signed | constraints.Float, V interface {
|
||||||
|
Add(V) V
|
||||||
|
Sub(V) V
|
||||||
|
Max(V) V
|
||||||
|
Min(V) V
|
||||||
|
Less(V) bool
|
||||||
|
More(V) bool
|
||||||
|
Sum() I
|
||||||
|
}] struct{ Upper, Lower V }
|
||||||
|
|
||||||
|
func (aabb AABB[I, V]) WithIn(point V) bool {
|
||||||
|
return aabb.Lower.Less(point) && aabb.Upper.More(point)
|
||||||
|
}
|
||||||
|
func (aabb AABB[I, V]) Touch(other AABB[I, V]) bool {
|
||||||
|
return aabb.Lower.Less(other.Upper) && other.Lower.Less(aabb.Upper) &&
|
||||||
|
aabb.Upper.More(other.Lower) && other.Upper.More(aabb.Lower)
|
||||||
|
}
|
||||||
|
func (aabb AABB[I, V]) Union(other AABB[I, V]) AABB[I, V] {
|
||||||
|
return AABB[I, V]{Upper: aabb.Upper.Max(other.Upper), Lower: aabb.Lower.Min(other.Lower)}
|
||||||
|
}
|
||||||
|
func (aabb AABB[I, V]) Surface() I { return aabb.Upper.Sub(aabb.Lower).Sum() * 2 }
|
||||||
|
|
||||||
|
type Sphere[I constraints.Float, V interface {
|
||||||
|
Add(V) V
|
||||||
|
Sub(V) V
|
||||||
|
Mul(I) V
|
||||||
|
Max(V) V
|
||||||
|
Min(V) V
|
||||||
|
Less(V) bool
|
||||||
|
More(V) bool
|
||||||
|
Norm() I
|
||||||
|
Sum() I
|
||||||
|
}] struct {
|
||||||
|
Center V
|
||||||
|
R I
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Sphere[I, V]) WithIn(point V) bool {
|
||||||
|
return s.Center.Sub(point).Norm() < s.R
|
||||||
|
}
|
||||||
|
func (s Sphere[I, V]) Touch(other Sphere[I, V]) bool {
|
||||||
|
return s.Center.Sub(other.Center).Norm() < s.R+other.R
|
||||||
|
}
|
||||||
|
func (s Sphere[I, V]) Union(other Sphere[I, V]) Sphere[I, V] {
|
||||||
|
d := other.Center.Sub(s.Center).Norm()
|
||||||
|
r1r2d := (s.R - other.R) / d
|
||||||
|
return Sphere[I, V]{
|
||||||
|
Center: s.Center.Mul(1 + r1r2d).Add(other.Center.Mul(1 - r1r2d)),
|
||||||
|
R: d + s.R + other.R,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (s Sphere[I, V]) Surface() I { return I(2 * math.Pi * s.R) }
|
38
server/internal/bvh/bound_test.go
Normal file
38
server/internal/bvh/bound_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package bvh
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestAABB_WithIn(t *testing.T) {
|
||||||
|
aabb := AABB[float64, Vec2[float64]]{
|
||||||
|
Upper: Vec2[float64]{2, 2},
|
||||||
|
Lower: Vec2[float64]{-1, -1},
|
||||||
|
}
|
||||||
|
if !aabb.WithIn(Vec2[float64]{0, 0}) {
|
||||||
|
panic("(0, 0) should included")
|
||||||
|
}
|
||||||
|
if aabb.WithIn(Vec2[float64]{-2, -2}) {
|
||||||
|
panic("(-2, -2) shouldn't included")
|
||||||
|
}
|
||||||
|
|
||||||
|
aabb2 := AABB[int, Vec3[int]]{
|
||||||
|
Upper: Vec3[int]{1, 1, 1},
|
||||||
|
Lower: Vec3[int]{-1, -1, -1},
|
||||||
|
}
|
||||||
|
if !aabb2.WithIn(Vec3[int]{0, 0, 0}) {
|
||||||
|
panic("(0, 0, 0) should included")
|
||||||
|
}
|
||||||
|
if aabb2.WithIn(Vec3[int]{-2, -2, 0}) {
|
||||||
|
panic("(-2, -2, 0) shouldn't included")
|
||||||
|
}
|
||||||
|
|
||||||
|
sphere := Sphere[float64, Vec2[float64]]{
|
||||||
|
Center: Vec2[float64]{0, 0},
|
||||||
|
R: 1.0,
|
||||||
|
}
|
||||||
|
if !sphere.WithIn(Vec2[float64]{0, 0}) {
|
||||||
|
t.Errorf("(0,0) is in")
|
||||||
|
}
|
||||||
|
if sphere.WithIn(Vec2[float64]{1, 1}) {
|
||||||
|
t.Errorf("(1,1) isn't in")
|
||||||
|
}
|
||||||
|
}
|
@ -1,158 +1,211 @@
|
|||||||
package bvh
|
package bvh
|
||||||
|
|
||||||
import (
|
//
|
||||||
"container/heap"
|
//import (
|
||||||
"math"
|
// "container/heap"
|
||||||
)
|
// "fmt"
|
||||||
|
// "golang.org/x/exp/constraints"
|
||||||
type Vec2 [2]float64
|
//)
|
||||||
|
//
|
||||||
func (v Vec2) Add(other Vec2) Vec2 { return Vec2{v[0] + other[0], v[1] + other[1]} }
|
//type Node[Vec constraints.Signed | constraints.Float, B interface {
|
||||||
func (v Vec2) Sub(other Vec2) Vec2 { return Vec2{v[0] - other[0], v[1] - other[1]} }
|
// WithIn(Vec) bool
|
||||||
func (v Vec2) Max(other Vec2) Vec2 { return Vec2{math.Max(v[0], other[0]), math.Max(v[1], other[1])} }
|
// Union(B) B
|
||||||
func (v Vec2) Min(other Vec2) Vec2 { return Vec2{math.Min(v[0], other[0]), math.Min(v[1], other[1])} }
|
// Surface() float64
|
||||||
|
//}, V any] struct {
|
||||||
type AABB2 struct{ Upper, Lower Vec2 }
|
// box B
|
||||||
|
// Value V
|
||||||
func (aabb AABB2) WithIn(point Vec2) bool {
|
// parent *Node[Vec, B, V]
|
||||||
return aabb.Lower[0] < point[0] && point[0] < aabb.Upper[0] &&
|
// children [2]*Node[Vec, B, V]
|
||||||
aabb.Lower[1] < point[1] && point[1] < aabb.Upper[1]
|
// isLeaf bool
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func (aabb AABB2) Union(other AABB2) AABB2 {
|
//func (n *Node[Vec, B, V]) findAnotherChild(not *Node[Vec, B, V]) *Node[Vec, B, V] {
|
||||||
return AABB2{
|
// if n.children[0] == not {
|
||||||
Upper: aabb.Upper.Max(other.Upper),
|
// return n.children[1]
|
||||||
Lower: aabb.Lower.Min(other.Lower),
|
// } else if n.children[1] == not {
|
||||||
}
|
// return n.children[0]
|
||||||
}
|
// }
|
||||||
|
// panic("unreachable, please make sure the 'not' is the n's child")
|
||||||
func (aabb AABB2) Surface() float64 {
|
//}
|
||||||
d := aabb.Upper.Sub(aabb.Lower)
|
//
|
||||||
return 2 * (d[0] + d[1])
|
//func (n *Node[Vec, B, V]) findChildPointer(child *Node[Vec, B, V]) **Node[Vec, B, V] {
|
||||||
}
|
// if n.children[0] == child {
|
||||||
|
// return &n.children[0]
|
||||||
type Node2 struct {
|
// } else if n.children[1] == child {
|
||||||
box AABB2
|
// return &n.children[1]
|
||||||
Value interface{}
|
// }
|
||||||
parent *Node2
|
// panic("unreachable, please make sure the 'not' is the n's child")
|
||||||
children [2]*Node2
|
//}
|
||||||
isLeaf bool
|
//
|
||||||
}
|
//type Tree[I constraints.Signed | constraints.Float, B interface {
|
||||||
|
// Union(B) B
|
||||||
func (n *Node2) findAnotherChild(not *Node2) *Node2 {
|
// Surface() I
|
||||||
if n.isLeaf {
|
//}, V any] struct {
|
||||||
return nil
|
// root *Node[I, B, V]
|
||||||
} else if n.children[0] == not {
|
//}
|
||||||
return n.children[1]
|
//
|
||||||
} else if n.children[1] == not {
|
//func (t *Tree[Vec, B, V]) Insert(leaf B, value V) (n *Node[Vec, B, V]) {
|
||||||
return n.children[0]
|
// n = &Node[Vec, B, V]{
|
||||||
}
|
// box: leaf,
|
||||||
panic("unreachable, please make sure the 'not' is the n's child")
|
// Value: value,
|
||||||
}
|
// parent: nil,
|
||||||
|
// children: [2]*Node[Vec, B, V]{},
|
||||||
type Tree2 struct {
|
// isLeaf: true,
|
||||||
root *Node2
|
// }
|
||||||
}
|
// if t.root == nil {
|
||||||
|
// t.root = n
|
||||||
func (t *Tree2) Insert(leaf AABB2) (n *Node2) {
|
// return
|
||||||
n = &Node2{
|
// }
|
||||||
box: leaf,
|
//
|
||||||
parent: nil,
|
// // Stage 1: find the best sibling for the new leaf
|
||||||
children: [2]*Node2{},
|
// sibling := t.root
|
||||||
isLeaf: true,
|
// bestCost := t.root.box.Union(leaf).Surface()
|
||||||
}
|
// parentTo := &t.root // the parent's children pointer which point to the sibling
|
||||||
if t.root == nil {
|
// queue := searchHeap[Node[Vec, B, V]]{searchItem[Node[Vec, B, V]]{pointer: t.root, parentTo: &t.root}}
|
||||||
t.root = n
|
//
|
||||||
return
|
// leafCost := leaf.Surface()
|
||||||
}
|
// for queue.Len() > 0 {
|
||||||
|
// p := heap.Pop(&queue).(searchItem[Node[Vec, B, V]])
|
||||||
// Stage 1: find the best sibling for the new leaf
|
// // determine if node p has the best cost
|
||||||
sibling := t.root
|
// mergeSurface := p.pointer.box.Union(leaf).Surface()
|
||||||
bestCost := t.root.box.Union(leaf).Surface()
|
// deltaCost := mergeSurface - p.pointer.box.Surface()
|
||||||
parentTo := &t.root // the parent's children pointer which point to the sibling
|
// cost := p.inheritedCost + mergeSurface
|
||||||
queue := searchHeap{searchItem{pointer: t.root, parentTo: &t.root}}
|
// if cost <= bestCost {
|
||||||
|
// bestCost = cost
|
||||||
leafCost := leaf.Surface()
|
// sibling = p.pointer
|
||||||
for len(queue) > 0 {
|
// parentTo = p.parentTo
|
||||||
p := heap.Pop(&queue).(searchItem)
|
// }
|
||||||
// determine if node p has the best cost
|
// // determine if it is worthwhile to explore the children of node p.
|
||||||
mergeSurface := p.pointer.box.Union(leaf).Surface()
|
// inheritedCost := p.inheritedCost + deltaCost // lower bound
|
||||||
deltaCost := mergeSurface - p.pointer.box.Surface()
|
// if !p.pointer.isLeaf && inheritedCost+leafCost < bestCost {
|
||||||
cost := p.inheritedCost + mergeSurface
|
// heap.Push(&queue, searchItem[Node[Vec, B, V]]{
|
||||||
if cost < bestCost {
|
// pointer: p.pointer.children[0],
|
||||||
bestCost = cost
|
// parentTo: &p.pointer.children[0],
|
||||||
sibling = p.pointer
|
// inheritedCost: inheritedCost,
|
||||||
parentTo = p.parentTo
|
// })
|
||||||
}
|
// heap.Push(&queue, searchItem[Node[Vec, B, V]]{
|
||||||
// determine if it is worthwhile to explore the children of node p.
|
// pointer: p.pointer.children[1],
|
||||||
inheritedCost := p.inheritedCost + deltaCost // lower bound
|
// parentTo: &p.pointer.children[1],
|
||||||
if inheritedCost+leafCost < bestCost {
|
// inheritedCost: inheritedCost,
|
||||||
if p.pointer.children[0] != nil {
|
// })
|
||||||
heap.Push(&queue, searchItem{
|
// }
|
||||||
pointer: p.pointer.children[0],
|
// }
|
||||||
parentTo: &p.pointer.children[0],
|
//
|
||||||
inheritedCost: inheritedCost,
|
// // Stage 2: create a new parent
|
||||||
})
|
// *parentTo = &Node[Vec, B, V]{
|
||||||
}
|
// box: sibling.box.Union(leaf), // we will calculate in Stage3
|
||||||
if p.pointer.children[1] != nil {
|
// parent: sibling.parent,
|
||||||
heap.Push(&queue, searchItem{
|
// children: [2]*Node[Vec, B, V]{sibling, n},
|
||||||
pointer: p.pointer.children[1],
|
// isLeaf: false,
|
||||||
parentTo: &p.pointer.children[1],
|
// }
|
||||||
inheritedCost: inheritedCost,
|
// n.parent = *parentTo
|
||||||
})
|
// sibling.parent = *parentTo
|
||||||
}
|
//
|
||||||
}
|
// // Stage 3: walk back up the tree refitting AABBs
|
||||||
}
|
// for p := *parentTo; p != nil; p = p.parent {
|
||||||
|
// p.box = p.children[0].box.Union(p.children[1].box)
|
||||||
// Stage 2: create a new parent
|
// t.rotate(p)
|
||||||
*parentTo = &Node2{
|
// }
|
||||||
box: sibling.box.Union(leaf), // we will calculate in Stage3
|
// return
|
||||||
parent: sibling.parent,
|
//}
|
||||||
children: [2]*Node2{sibling, n},
|
//
|
||||||
isLeaf: false,
|
//func (t *Tree[Vec, B, V]) Delete(n *Node[Vec, B, V]) interface{} {
|
||||||
}
|
// if n.parent == nil {
|
||||||
n.parent = *parentTo
|
// // n is the root
|
||||||
sibling.parent = *parentTo
|
// t.root = nil
|
||||||
|
// return n.Value
|
||||||
// Stage 3: walk back up the tree refitting AABBs
|
// }
|
||||||
for p := *parentTo; p.parent != nil; p = p.parent {
|
// sibling := n.parent.findAnotherChild(n)
|
||||||
p.box = p.children[0].box.Union(p.children[1].box)
|
// grand := n.parent.parent
|
||||||
//TODO: t.rotate(p)
|
// if grand == nil {
|
||||||
}
|
// // n's parent is root
|
||||||
return
|
// t.root = sibling
|
||||||
}
|
// sibling.parent = nil
|
||||||
|
// } else {
|
||||||
func (t *Tree2) Find(point Vec2, f func(*Node2)) {
|
// p := grand.findChildPointer(n.parent)
|
||||||
t.root.lookup(point, f)
|
// *p = sibling
|
||||||
}
|
// sibling.parent = grand
|
||||||
|
// for p := sibling.parent; p.parent != nil; p = p.parent {
|
||||||
func (n *Node2) lookup(point Vec2, f func(node2 *Node2)) {
|
// p.box = p.children[0].box.Union(p.children[1].box)
|
||||||
if n == nil || !n.box.WithIn(point) {
|
// t.rotate(p)
|
||||||
return
|
// }
|
||||||
}
|
// }
|
||||||
if n.isLeaf {
|
// return n.Value
|
||||||
f(n)
|
//}
|
||||||
} else {
|
//
|
||||||
n.children[0].lookup(point, f)
|
//func (t *Tree[Vec, B, V]) rotate(n *Node[Vec, B, V]) {
|
||||||
n.children[1].lookup(point, f)
|
// if n.isLeaf || n.parent == nil {
|
||||||
}
|
// return
|
||||||
}
|
// }
|
||||||
|
// // trying to swap n's sibling and children
|
||||||
type searchHeap []searchItem
|
// sibling := n.parent.findAnotherChild(n)
|
||||||
type searchItem struct {
|
// current := n.box.Surface()
|
||||||
pointer *Node2
|
// if n.children[1].box.Union(sibling.box).Surface() < current {
|
||||||
parentTo **Node2
|
// // swap n.children[0] and sibling
|
||||||
inheritedCost float64
|
// n.parent.children, n.children, n.children[0].parent, sibling.parent = [2]*Node[Vec, B, V]{n, n.children[0]}, [2]*Node[Vec, B, V]{sibling, n.children[1]}, n.parent, n
|
||||||
}
|
// n.box = n.children[0].box.Union(n.children[1].box)
|
||||||
|
// } else if n.children[0].box.Union(sibling.box).Surface() < current {
|
||||||
func (h searchHeap) Len() int { return len(h) }
|
// // swap n.children[1] and sibling
|
||||||
func (h searchHeap) Less(i, j int) bool { return h[i].inheritedCost < h[j].inheritedCost }
|
// n.parent.children, n.children, n.children[1].parent, sibling.parent = [2]*Node[Vec, B, V]{n, n.children[1]}, [2]*Node[Vec, B, V]{sibling, n.children[0]}, n.parent, n
|
||||||
func (h searchHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
// n.box = n.children[0].box.Union(n.children[1].box)
|
||||||
func (h *searchHeap) Push(x interface{}) { *h = append(*h, x.(searchItem)) }
|
// }
|
||||||
func (h *searchHeap) Pop() interface{} {
|
//}
|
||||||
old := *h
|
//
|
||||||
n := len(old)
|
////func lookupPoint[B interface {
|
||||||
x := old[n-1]
|
//// Union(B) B
|
||||||
*h = old[0 : n-1]
|
//// Surface() float64
|
||||||
return x
|
////}, V any](n *Node[B, V], point Vec2, f func(v V)) {
|
||||||
}
|
//// if n == nil || !n.box.WithIn(point) {
|
||||||
|
//// return
|
||||||
|
//// }
|
||||||
|
//// if n.isLeaf {
|
||||||
|
//// f(n.Value)
|
||||||
|
//// } else {
|
||||||
|
//// lookupVec(n.children[0], point, f)
|
||||||
|
//// lookupVec(n.children[1], point, f)
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
//
|
||||||
|
////
|
||||||
|
////func lookupAABB(n *Node, aabb AABB, f func(v interface{})) {
|
||||||
|
//// if n == nil || !n.box.Touch(aabb) {
|
||||||
|
//// return
|
||||||
|
//// }
|
||||||
|
//// if n.isLeaf {
|
||||||
|
//// f(n.Value)
|
||||||
|
//// } else {
|
||||||
|
//// lookupAABB(n.children[0], aabb, f)
|
||||||
|
//// lookupAABB(n.children[1], aabb, f)
|
||||||
|
//// }
|
||||||
|
////}
|
||||||
|
//
|
||||||
|
//type searchHeap[V any] []searchItem[V]
|
||||||
|
//type searchItem[V any] struct {
|
||||||
|
// pointer *V
|
||||||
|
// parentTo **V
|
||||||
|
// inheritedCost float64
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (h searchHeap[V]) Len() int { return len(h) }
|
||||||
|
//func (h searchHeap[V]) Less(i, j int) bool { return h[i].inheritedCost < h[j].inheritedCost }
|
||||||
|
//func (h searchHeap[V]) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||||
|
//func (h *searchHeap[V]) Push(x interface{}) { *h = append(*h, x.(searchItem[V])) }
|
||||||
|
//func (h *searchHeap[V]) Pop() interface{} {
|
||||||
|
// old := *h
|
||||||
|
// n := len(old)
|
||||||
|
// x := old[n-1]
|
||||||
|
// *h = old[0 : n-1]
|
||||||
|
// return x
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (t Tree[Vec, B, V]) String() string {
|
||||||
|
// return t.root.String()
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (n *Node[Vec, B, V]) String() string {
|
||||||
|
// if n.isLeaf {
|
||||||
|
// return fmt.Sprint(n.Value)
|
||||||
|
// } else {
|
||||||
|
// return fmt.Sprintf("{%v, %v}", n.children[0], n.children[1])
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
@ -1,107 +1,148 @@
|
|||||||
package bvh
|
package bvh
|
||||||
|
|
||||||
import (
|
//
|
||||||
"fmt"
|
//import (
|
||||||
"math/rand"
|
// "math/rand"
|
||||||
"strings"
|
// "testing"
|
||||||
"testing"
|
//)
|
||||||
)
|
//
|
||||||
|
//func TestTree2_Insert(t *testing.T) {
|
||||||
func TestTree2_Insert(t *testing.T) {
|
// aabbs := []AABB[Vec2[float64]]{
|
||||||
aabbs := []AABB2{
|
// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{0, 0}},
|
||||||
{Upper: Vec2{1, 1}, Lower: Vec2{0, 0}},
|
// {Upper: Vec2[float64]{2, 1}, Lower: Vec2[float64]{1, 0}},
|
||||||
{Upper: Vec2{2, 1}, Lower: Vec2{1, 0}},
|
// {Upper: Vec2[float64]{11, 1}, Lower: Vec2[float64]{10, 0}},
|
||||||
{Upper: Vec2{11, 1}, Lower: Vec2{10, 0}},
|
// {Upper: Vec2[float64]{12, 1}, Lower: Vec2[float64]{11, 0}},
|
||||||
{Upper: Vec2{12, 1}, Lower: Vec2{11, 0}},
|
// {Upper: Vec2[float64]{101, 1}, Lower: Vec2[float64]{100, 0}},
|
||||||
{Upper: Vec2{101, 1}, Lower: Vec2{100, 0}},
|
// {Upper: Vec2[float64]{102, 1}, Lower: Vec2[float64]{101, 0}},
|
||||||
{Upper: Vec2{102, 1}, Lower: Vec2{101, 0}},
|
// {Upper: Vec2[float64]{111, 1}, Lower: Vec2[float64]{110, 0}},
|
||||||
{Upper: Vec2{111, 1}, Lower: Vec2{110, 0}},
|
// {Upper: Vec2[float64]{112, 1}, Lower: Vec2[float64]{111, 0}},
|
||||||
{Upper: Vec2{112, 1}, Lower: Vec2{111, 0}},
|
// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{-1, -1}},
|
||||||
{Upper: Vec2{1, 1}, Lower: Vec2{-1, -1}},
|
// }
|
||||||
}
|
// var bvh Tree[float64, AABB[Vec2[float64]], int]
|
||||||
var bvh Tree2
|
// for i, aabb := range aabbs {
|
||||||
for _, aabb := range aabbs {
|
// bvh.Insert(aabb, i)
|
||||||
bvh.Insert(aabb)
|
// // visualize
|
||||||
// visualize
|
// t.Log(bvh)
|
||||||
var sb strings.Builder
|
// }
|
||||||
toString(&sb, bvh.root)
|
// //bvh.FindVec(Vec2{0.5, 0.5}, func(v interface{}) {
|
||||||
t.Log(sb.String())
|
// // t.Logf("find! %v", v)
|
||||||
}
|
// //})
|
||||||
bvh.Find(Vec2{0.5, 0.5}, func(n *Node2) {
|
//}
|
||||||
t.Logf("find! %v", n.box)
|
//
|
||||||
})
|
//func TestTree2_FindVec(t *testing.T) {
|
||||||
}
|
// aabbs := []AABB[Vec2[float64]]{
|
||||||
|
// {Upper: Vec2[float64]{2, 2}, Lower: Vec2[float64]{-1, -1}},
|
||||||
func TestTree2_Insert2_notLinkedTable(t *testing.T) {
|
// {Upper: Vec2[float64]{2, 1}, Lower: Vec2[float64]{-1, -2}},
|
||||||
const items = 1000
|
// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{-2, -2}},
|
||||||
var bvh Tree2
|
// {Upper: Vec2[float64]{1, 2}, Lower: Vec2[float64]{-2, -1}},
|
||||||
for i := 0; i < items; i++ {
|
// }
|
||||||
pos := Vec2{0, float64(i)}
|
// var bvh Tree[float64, AABB[Vec2[float64]], int]
|
||||||
bvh.Insert(AABB2{
|
// for i, aabb := range aabbs {
|
||||||
Upper: Vec2{pos[0] + 1, pos[1] + 1},
|
// bvh.Insert(aabb, i)
|
||||||
Lower: Vec2{pos[0], pos[1]},
|
// // visualize
|
||||||
})
|
// t.Log(bvh)
|
||||||
}
|
// }
|
||||||
//calc depth
|
// //findVec := func(vec Vec2) (list []interface{}) {
|
||||||
if depth := lookupDepth(bvh.root); depth > items/2 {
|
// // bvh.FindVec(vec, func(v interface{}) { list = append(list, v) })
|
||||||
t.Errorf("the bvh is unbalanced: depth %d", depth)
|
// // return
|
||||||
} else {
|
// //}
|
||||||
t.Logf("the depth of bvh with %d element is %d", items, depth)
|
// //t.Log(findVec(Vec2{0, 0}))
|
||||||
}
|
// //t.Log(findVec(Vec2{1.5, 0}))
|
||||||
}
|
// //t.Log(findVec(Vec2{1.5, 1.5}))
|
||||||
|
// //t.Log(findVec(Vec2{-1.5, 0}))
|
||||||
func toString(sb *strings.Builder, n *Node2) {
|
// //
|
||||||
if n.isLeaf {
|
// //findAABB := func(aabb AABB2) (list []interface{}) {
|
||||||
_, _ = fmt.Fprintf(sb, "(%v,%v)", n.box.Upper[1], n.box.Upper[0])
|
// // bvh.FindAABB(aabb, func(v interface{}) { list = append(list, v) })
|
||||||
return
|
// // return
|
||||||
}
|
// //}
|
||||||
sb.WriteByte('{')
|
// //t.Log(findAABB(AABB2{Upper: Vec2{1, 1}, Lower: Vec2{-1, -1}}))
|
||||||
v1 := n.children[0]
|
// //t.Log(findAABB(AABB2{Upper: Vec2{3, 3}, Lower: Vec2{1.5, 1.5}}))
|
||||||
if v1 != nil {
|
// //t.Log(findAABB(AABB2{Upper: Vec2{-1.5, 0.5}, Lower: Vec2{-2.5, -0.5}}))
|
||||||
toString(sb, v1)
|
//}
|
||||||
}
|
//
|
||||||
v2 := n.children[1]
|
//func TestTree2_Insert_rotation(t *testing.T) {
|
||||||
if v2 != nil {
|
// var bvh Tree[float64, AABB[Vec2[float64]], int]
|
||||||
if v1 != nil {
|
// for i := 0; i < 5; i++ {
|
||||||
sb.WriteString(", ")
|
// bvh.Insert(AABB[Vec2[float64]]{
|
||||||
}
|
// Upper: Vec2[float64]{float64(i), float64(i)},
|
||||||
toString(sb, v2)
|
// Lower: Vec2[float64]{float64(0), float64(0)},
|
||||||
}
|
// }, i)
|
||||||
sb.WriteByte('}')
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func lookupDepth(n *Node2) int {
|
//func BenchmarkTree2_Insert_random(b *testing.B) {
|
||||||
depth := 0
|
// const size = 25
|
||||||
for _, child := range n.children {
|
// // generate test cases
|
||||||
if child != nil {
|
// aabbs := make([]AABB[Vec2[float64]], b.N)
|
||||||
subdepth := lookupDepth(child)
|
// poses := make([]Vec2[float64], b.N)
|
||||||
if subdepth > depth {
|
// for i := range aabbs {
|
||||||
depth = subdepth
|
// poses[i] = Vec2[float64]{rand.Float64() * 1e4, rand.Float64() * 1e4}
|
||||||
}
|
// aabbs[i] = AABB[Vec2[float64]]{
|
||||||
}
|
// Upper: Vec2[float64]{poses[i][0] + size, poses[i][0] + size},
|
||||||
}
|
// Lower: Vec2[float64]{poses[i][0] - size, poses[i][0] - size},
|
||||||
return depth + 1 // add itself
|
// }
|
||||||
}
|
// }
|
||||||
|
// b.ResetTimer()
|
||||||
func BenchmarkTree2_Insert(b *testing.B) {
|
//
|
||||||
const size = 25
|
// var bvh Tree[float64, AABB[Vec2[float64]], any]
|
||||||
// generate test cases
|
// for _, v := range aabbs {
|
||||||
aabbs := make([]AABB2, b.N)
|
// bvh.Insert(v, nil)
|
||||||
poses := make([]Vec2, b.N)
|
// }
|
||||||
for i := range aabbs {
|
// //for _, v := range poses {
|
||||||
poses[i] = Vec2{rand.Float64() * 1e4, rand.Float64() * 1e4}
|
// // bvh.FindVec(v, func(interface{}) {})
|
||||||
aabbs[i] = AABB2{
|
// //}
|
||||||
Upper: Vec2{poses[i][0] + size, poses[i][0] + size},
|
//}
|
||||||
Lower: Vec2{poses[i][0] - size, poses[i][0] - size},
|
//
|
||||||
}
|
//func BenchmarkTree2_Insert_sorted1(b *testing.B) {
|
||||||
}
|
// // generate test cases
|
||||||
b.ResetTimer()
|
// var bvh Tree[float64, AABB[Vec2[float64]], int]
|
||||||
|
// upper := Vec2[float64]{float64(b.N), float64(b.N)}
|
||||||
var bvh Tree2
|
// for i := 0; i < b.N; i++ {
|
||||||
for _, v := range aabbs {
|
// bvh.Insert(AABB[Vec2[float64]]{
|
||||||
bvh.Insert(v)
|
// Upper: upper,
|
||||||
}
|
// Lower: Vec2[float64]{float64(i), float64(i)},
|
||||||
for _, v := range poses {
|
// }, i)
|
||||||
bvh.Find(v, func(n *Node2) {})
|
// }
|
||||||
}
|
//}
|
||||||
}
|
//
|
||||||
|
//func BenchmarkTree2_Insert_sorted2(b *testing.B) {
|
||||||
|
// // generate test cases
|
||||||
|
// var bvh Tree[float64, AABB[Vec2[float64]], int]
|
||||||
|
// for i := 0; i < b.N; i++ {
|
||||||
|
// bvh.Insert(AABB[Vec2[float64]]{
|
||||||
|
// Upper: Vec2[float64]{float64(i), float64(i)},
|
||||||
|
// Lower: Vec2[float64]{0, 0},
|
||||||
|
// }, i)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func BenchmarkTree2_Delete_random(b *testing.B) {
|
||||||
|
// const size = 25
|
||||||
|
// // generate test cases
|
||||||
|
// aabbs := make([]AABB[Vec2[float64]], b.N)
|
||||||
|
// poses := make([]Vec2[float64], b.N)
|
||||||
|
// nodes := make([]*Node[AABB[Vec2[float64]], any], b.N)
|
||||||
|
// for i := range aabbs {
|
||||||
|
// poses[i] = Vec2[float64]{rand.Float64() * 1e4, rand.Float64() * 1e4}
|
||||||
|
// aabbs[i] = AABB[Vec2[float64]]{
|
||||||
|
// Upper: Vec2[float64]{poses[i][0] + size, poses[i][0] + size},
|
||||||
|
// Lower: Vec2[float64]{poses[i][0] - size, poses[i][0] - size},
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// b.ResetTimer()
|
||||||
|
//
|
||||||
|
// var bvh Tree[float64, AABB[Vec2[float64]], any]
|
||||||
|
// for i, v := range aabbs {
|
||||||
|
// nodes[i] = bvh.Insert(v, nil)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// b.StopTimer()
|
||||||
|
// rand.Shuffle(b.N, func(i, j int) {
|
||||||
|
// nodes[i], nodes[j] = nodes[j], nodes[i]
|
||||||
|
// })
|
||||||
|
// b.StartTimer()
|
||||||
|
//
|
||||||
|
// for _, v := range nodes {
|
||||||
|
// bvh.Delete(v)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
56
server/internal/bvh/vector.go
Normal file
56
server/internal/bvh/vector.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package bvh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Vec2[I constraints.Signed | constraints.Float] [2]I
|
||||||
|
|
||||||
|
func (v Vec2[I]) Add(other Vec2[I]) Vec2[I] { return Vec2[I]{v[0] + other[0], v[1] + other[1]} }
|
||||||
|
func (v Vec2[I]) Sub(other Vec2[I]) Vec2[I] { return Vec2[I]{v[0] - other[0], v[1] - other[1]} }
|
||||||
|
func (v Vec2[I]) Mul(i I) Vec2[I] { return Vec2[I]{v[0] * i, v[1] * i} }
|
||||||
|
func (v Vec2[I]) Max(other Vec2[I]) Vec2[I] { return Vec2[I]{max(v[0], other[0]), max(v[1], other[1])} }
|
||||||
|
func (v Vec2[I]) Min(other Vec2[I]) Vec2[I] { return Vec2[I]{min(v[0], other[0]), min(v[1], other[1])} }
|
||||||
|
func (v Vec2[I]) Less(other Vec2[I]) bool { return v[0] < other[0] && v[1] < other[1] }
|
||||||
|
func (v Vec2[I]) More(other Vec2[I]) bool { return v[0] > other[0] && v[1] > other[1] }
|
||||||
|
func (v Vec2[I]) Norm() float64 { return sqrt(v[0]*v[0] + v[1]*v[1]) }
|
||||||
|
func (v Vec2[I]) Sum() I { return v[0] + v[1] }
|
||||||
|
|
||||||
|
type Vec3[I constraints.Signed | constraints.Float] [3]I
|
||||||
|
|
||||||
|
func (v Vec3[I]) Add(other Vec3[I]) Vec3[I] {
|
||||||
|
return Vec3[I]{v[0] + other[0], v[1] + other[1], v[2] + other[2]}
|
||||||
|
}
|
||||||
|
func (v Vec3[I]) Sub(other Vec3[I]) Vec3[I] {
|
||||||
|
return Vec3[I]{v[0] - other[0], v[1] - other[1], v[2] - other[2]}
|
||||||
|
}
|
||||||
|
func (v Vec3[I]) Mul(i I) Vec3[I] { return Vec3[I]{v[0] * i, v[1] * i, v[2] * i} }
|
||||||
|
func (v Vec3[I]) Max(other Vec3[I]) Vec3[I] {
|
||||||
|
return Vec3[I]{max(v[0], other[0]), max(v[1], other[1]), max(v[2], other[2])}
|
||||||
|
}
|
||||||
|
func (v Vec3[I]) Min(other Vec3[I]) Vec3[I] {
|
||||||
|
return Vec3[I]{min(v[0], other[0]), min(v[1], other[1]), min(v[2], other[2])}
|
||||||
|
}
|
||||||
|
func (v Vec3[I]) Less(other Vec3[I]) bool { return v[0] < other[0] && v[1] < other[1] }
|
||||||
|
func (v Vec3[I]) More(other Vec3[I]) bool { return v[0] > other[0] && v[1] > other[1] }
|
||||||
|
func (v Vec3[I]) Norm() float64 { return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]) }
|
||||||
|
func (v Vec3[I]) Sum() I { return v[0] + v[1] }
|
||||||
|
|
||||||
|
func max[T constraints.Ordered](a, b T) T {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func min[T constraints.Ordered](a, b T) T {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func sqrt[T constraints.Signed | constraints.Float](v T) float64 {
|
||||||
|
return math.Sqrt(float64(v))
|
||||||
|
}
|
Reference in New Issue
Block a user