BVH implement with less generic parm

This commit is contained in:
Tnze
2022-03-18 12:37:33 +08:00
parent 00de623cc2
commit 7997a5faea
2 changed files with 357 additions and 355 deletions

View File

@ -1,211 +1,217 @@
package bvh package bvh
// import (
//import ( "container/heap"
// "container/heap" "fmt"
// "fmt" "golang.org/x/exp/constraints"
// "golang.org/x/exp/constraints" )
//)
// type Node[I constraints.Float, B interface {
//type Node[Vec constraints.Signed | constraints.Float, B interface { Union(B) B
// WithIn(Vec) bool Surface() I
// Union(B) B }, V any] struct {
// Surface() float64 box B
//}, V any] struct { Value V
// box B parent *Node[I, B, V]
// Value V children [2]*Node[I, B, V]
// parent *Node[Vec, B, V] isLeaf bool
// children [2]*Node[Vec, B, V] }
// isLeaf bool
//} func (n *Node[I, B, V]) findAnotherChild(not *Node[I, B, V]) *Node[I, B, V] {
// if n.children[0] == not {
//func (n *Node[Vec, B, V]) findAnotherChild(not *Node[Vec, B, V]) *Node[Vec, B, V] { return n.children[1]
// if n.children[0] == not { } else if n.children[1] == not {
// return n.children[1] return n.children[0]
// } else if n.children[1] == not { }
// return n.children[0] panic("unreachable, please make sure the 'not' is the n's child")
// } }
// panic("unreachable, please make sure the 'not' is the n's child")
//} func (n *Node[I, B, V]) findChildPointer(child *Node[I, B, V]) **Node[I, B, V] {
// if n.children[0] == child {
//func (n *Node[Vec, B, V]) findChildPointer(child *Node[Vec, B, V]) **Node[Vec, B, V] { return &n.children[0]
// if n.children[0] == child { } else if n.children[1] == child {
// return &n.children[0] return &n.children[1]
// } else if n.children[1] == child { }
// return &n.children[1] panic("unreachable, please make sure the 'not' is the n's child")
// } }
// panic("unreachable, please make sure the 'not' is the n's child")
//} func (n *Node[I, B, V]) each(test func(bound B) bool, foreach func(v V)) {
// if n == nil {
//type Tree[I constraints.Signed | constraints.Float, B interface { return
// Union(B) B }
// Surface() I if n.isLeaf {
//}, V any] struct { if test(n.box) {
// root *Node[I, B, V] foreach(n.Value)
//} }
// } else {
//func (t *Tree[Vec, B, V]) Insert(leaf B, value V) (n *Node[Vec, B, V]) { n.children[0].each(test, foreach)
// n = &Node[Vec, B, V]{ n.children[1].each(test, foreach)
// box: leaf, }
// Value: value, }
// parent: nil,
// children: [2]*Node[Vec, B, V]{}, type Tree[I constraints.Float, B interface {
// isLeaf: true, Union(B) B
// } Surface() I
// if t.root == nil { }, V any] struct {
// t.root = n root *Node[I, B, V]
// return }
// }
// func (t *Tree[I, B, V]) Insert(leaf B, value V) (n *Node[I, B, V]) {
// // Stage 1: find the best sibling for the new leaf n = &Node[I, B, V]{
// sibling := t.root box: leaf,
// bestCost := t.root.box.Union(leaf).Surface() Value: value,
// parentTo := &t.root // the parent's children pointer which point to the sibling parent: nil,
// queue := searchHeap[Node[Vec, B, V]]{searchItem[Node[Vec, B, V]]{pointer: t.root, parentTo: &t.root}} children: [2]*Node[I, B, V]{nil, nil},
// isLeaf: true,
// leafCost := leaf.Surface() }
// for queue.Len() > 0 { if t.root == nil {
// p := heap.Pop(&queue).(searchItem[Node[Vec, B, V]]) t.root = n
// // determine if node p has the best cost return
// mergeSurface := p.pointer.box.Union(leaf).Surface() }
// deltaCost := mergeSurface - p.pointer.box.Surface()
// cost := p.inheritedCost + mergeSurface // Stage 1: find the best sibling for the new leaf
// if cost <= bestCost { sibling := t.root
// bestCost = cost bestCost := t.root.box.Union(leaf).Surface()
// sibling = p.pointer parentTo := &t.root // the parent's children pointer which point to the sibling
// parentTo = p.parentTo
// } var queue searchHeap[I, Node[I, B, V]]
// // determine if it is worthwhile to explore the children of node p. queue.Push(searchItem[I, Node[I, B, V]]{pointer: t.root, parentTo: &t.root})
// inheritedCost := p.inheritedCost + deltaCost // lower bound
// if !p.pointer.isLeaf && inheritedCost+leafCost < bestCost { leafCost := leaf.Surface()
// heap.Push(&queue, searchItem[Node[Vec, B, V]]{ for queue.Len() > 0 {
// pointer: p.pointer.children[0], p := heap.Pop(&queue).(searchItem[I, Node[I, B, V]])
// parentTo: &p.pointer.children[0], // determine if node p has the best cost
// inheritedCost: inheritedCost, mergeSurface := p.pointer.box.Union(leaf).Surface()
// }) deltaCost := mergeSurface - p.pointer.box.Surface()
// heap.Push(&queue, searchItem[Node[Vec, B, V]]{ cost := p.inheritedCost + mergeSurface
// pointer: p.pointer.children[1], if cost <= bestCost {
// parentTo: &p.pointer.children[1], bestCost = cost
// inheritedCost: inheritedCost, sibling = p.pointer
// }) parentTo = p.parentTo
// } }
// } // determine if it is worthwhile to explore the children of node p.
// inheritedCost := p.inheritedCost + deltaCost // lower bound
// // Stage 2: create a new parent if !p.pointer.isLeaf && inheritedCost+leafCost < bestCost {
// *parentTo = &Node[Vec, B, V]{ heap.Push(&queue, searchItem[I, Node[I, B, V]]{
// box: sibling.box.Union(leaf), // we will calculate in Stage3 pointer: p.pointer.children[0],
// parent: sibling.parent, parentTo: &p.pointer.children[0],
// children: [2]*Node[Vec, B, V]{sibling, n}, inheritedCost: inheritedCost,
// isLeaf: false, })
// } heap.Push(&queue, searchItem[I, Node[I, B, V]]{
// n.parent = *parentTo pointer: p.pointer.children[1],
// sibling.parent = *parentTo parentTo: &p.pointer.children[1],
// inheritedCost: inheritedCost,
// // 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) }
// t.rotate(p)
// } // Stage 2: create a new parent
// return *parentTo = &Node[I, B, V]{
//} box: sibling.box.Union(leaf), // we will calculate in Stage3
// parent: sibling.parent,
//func (t *Tree[Vec, B, V]) Delete(n *Node[Vec, B, V]) interface{} { children: [2]*Node[I, B, V]{sibling, n},
// if n.parent == nil { isLeaf: false,
// // n is the root }
// t.root = nil n.parent = *parentTo
// return n.Value sibling.parent = *parentTo
// }
// sibling := n.parent.findAnotherChild(n) // Stage 3: walk back up the tree refitting AABBs
// grand := n.parent.parent for p := *parentTo; p != nil; p = p.parent {
// if grand == nil { p.box = p.children[0].box.Union(p.children[1].box)
// // n's parent is root t.rotate(p)
// t.root = sibling }
// sibling.parent = nil return
// } else { }
// p := grand.findChildPointer(n.parent)
// *p = sibling func (t *Tree[I, B, V]) Delete(n *Node[I, B, V]) interface{} {
// sibling.parent = grand if n.parent == nil {
// for p := sibling.parent; p.parent != nil; p = p.parent { // n is the root
// p.box = p.children[0].box.Union(p.children[1].box) t.root = nil
// t.rotate(p) return n.Value
// } }
// } sibling := n.parent.findAnotherChild(n)
// return n.Value grand := n.parent.parent
//} if grand == nil {
// // n's parent is root
//func (t *Tree[Vec, B, V]) rotate(n *Node[Vec, B, V]) { t.root = sibling
// if n.isLeaf || n.parent == nil { sibling.parent = nil
// return } else {
// } p := grand.findChildPointer(n.parent)
// // trying to swap n's sibling and children *p = sibling
// sibling := n.parent.findAnotherChild(n) sibling.parent = grand
// current := n.box.Surface() for p := sibling.parent; p.parent != nil; p = p.parent {
// if n.children[1].box.Union(sibling.box).Surface() < current { p.box = p.children[0].box.Union(p.children[1].box)
// // swap n.children[0] and sibling t.rotate(p)
// 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 { return n.Value
// // swap n.children[1] and sibling }
// 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
// n.box = n.children[0].box.Union(n.children[1].box) func (t *Tree[I, B, V]) rotate(n *Node[I, B, V]) {
// } if n.isLeaf || n.parent == nil {
//} return
// }
////func lookupPoint[B interface { // trying to swap n's sibling and children
//// Union(B) B sibling := n.parent.findAnotherChild(n)
//// Surface() float64 current := n.box.Surface()
////}, V any](n *Node[B, V], point Vec2, f func(v V)) { if n.children[1].box.Union(sibling.box).Surface() < current {
//// if n == nil || !n.box.WithIn(point) { // swap n.children[0] and sibling
//// return t1 := [2]*Node[I, B, V]{n, n.children[0]}
//// } t2 := [2]*Node[I, B, V]{sibling, n.children[1]}
//// if n.isLeaf { n.parent.children, n.children, n.children[0].parent, sibling.parent = t1, t2, n.parent, n
//// f(n.Value) n.box = n.children[0].box.Union(n.children[1].box)
//// } else { } else if n.children[0].box.Union(sibling.box).Surface() < current {
//// lookupVec(n.children[0], point, f) // swap n.children[1] and sibling
//// lookupVec(n.children[1], point, f) t1 := [2]*Node[I, B, V]{n, n.children[1]}
//// } t2 := [2]*Node[I, B, V]{sibling, n.children[0]}
////} n.parent.children, n.children, n.children[1].parent, sibling.parent = t1, t2, n.parent, n
// n.box = n.children[0].box.Union(n.children[1].box)
//// }
////func lookupAABB(n *Node, aabb AABB, f func(v interface{})) { }
//// if n == nil || !n.box.Touch(aabb) {
//// return func (t *Tree[I, B, V]) Find(test func(bound B) bool, foreach func(v V)) {
//// } t.root.each(test, foreach)
//// if n.isLeaf { }
//// f(n.Value)
//// } else { func (t Tree[I, B, V]) String() string {
//// lookupAABB(n.children[0], aabb, f) return t.root.String()
//// lookupAABB(n.children[1], aabb, f) }
//// }
////} func (n *Node[I, B, V]) String() string {
// if n.isLeaf {
//type searchHeap[V any] []searchItem[V] return fmt.Sprint(n.Value)
//type searchItem[V any] struct { } else {
// pointer *V return fmt.Sprintf("{%v, %v}", n.children[0], n.children[1])
// parentTo **V }
// inheritedCost float64 }
//}
// func TouchPoint[Vec any, B interface{ WithIn(Vec) bool }](point Vec) func(bound B) bool {
//func (h searchHeap[V]) Len() int { return len(h) } return func(bound B) bool {
//func (h searchHeap[V]) Less(i, j int) bool { return h[i].inheritedCost < h[j].inheritedCost } return bound.WithIn(point)
//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 func TouchBound[Vec any, B interface{ Touch(B) bool }](other B) func(bound B) bool {
// n := len(old) return func(bound B) bool {
// x := old[n-1] return bound.Touch(other)
// *h = old[0 : n-1] }
// return x }
//}
// type searchHeap[I constraints.Float, V any] []searchItem[I, V]
//func (t Tree[Vec, B, V]) String() string { type searchItem[I constraints.Float, V any] struct {
// return t.root.String() pointer *V
//} parentTo **V
// inheritedCost I
//func (n *Node[Vec, B, V]) String() string { }
// if n.isLeaf {
// return fmt.Sprint(n.Value) func (h searchHeap[I, V]) Len() int { return len(h) }
// } else { func (h searchHeap[I, V]) Less(i, j int) bool { return h[i].inheritedCost < h[j].inheritedCost }
// return fmt.Sprintf("{%v, %v}", n.children[0], n.children[1]) func (h searchHeap[I, V]) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
// } func (h *searchHeap[I, V]) Push(x interface{}) { *h = append(*h, x.(searchItem[I, V])) }
//} func (h *searchHeap[I, V]) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}

View File

@ -1,148 +1,144 @@
package bvh package bvh
// import (
//import ( "math/rand"
// "math/rand" "testing"
// "testing" )
//)
// func TestTree2_Insert(t *testing.T) {
//func TestTree2_Insert(t *testing.T) { aabbs := []AABB[float64, Vec2[float64]]{
// aabbs := []AABB[Vec2[float64]]{ {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{0, 0}},
// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{0, 0}}, {Upper: Vec2[float64]{2, 1}, Lower: Vec2[float64]{1, 0}},
// {Upper: Vec2[float64]{2, 1}, Lower: Vec2[float64]{1, 0}}, {Upper: Vec2[float64]{11, 1}, Lower: Vec2[float64]{10, 0}},
// {Upper: Vec2[float64]{11, 1}, Lower: Vec2[float64]{10, 0}}, {Upper: Vec2[float64]{12, 1}, Lower: Vec2[float64]{11, 0}},
// {Upper: Vec2[float64]{12, 1}, Lower: Vec2[float64]{11, 0}}, {Upper: Vec2[float64]{101, 1}, Lower: Vec2[float64]{100, 0}},
// {Upper: Vec2[float64]{101, 1}, Lower: Vec2[float64]{100, 0}}, {Upper: Vec2[float64]{102, 1}, Lower: Vec2[float64]{101, 0}},
// {Upper: Vec2[float64]{102, 1}, Lower: Vec2[float64]{101, 0}}, {Upper: Vec2[float64]{111, 1}, Lower: Vec2[float64]{110, 0}},
// {Upper: Vec2[float64]{111, 1}, Lower: Vec2[float64]{110, 0}}, {Upper: Vec2[float64]{112, 1}, Lower: Vec2[float64]{111, 0}},
// {Upper: Vec2[float64]{112, 1}, Lower: Vec2[float64]{111, 0}}, {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{-1, -1}},
// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{-1, -1}}, }
// } var bvh Tree[float64, AABB[float64, Vec2[float64]], int]
// var bvh Tree[float64, AABB[Vec2[float64]], int] for i, aabb := range aabbs {
// for i, aabb := range aabbs { bvh.Insert(aabb, i)
// bvh.Insert(aabb, i) // visualize
// // visualize t.Log(bvh)
// t.Log(bvh) }
// } bvh.Find(TouchPoint[Vec2[float64], AABB[float64, Vec2[float64]]](Vec2[float64]{0.5, 0.5}), func(v int) {
// //bvh.FindVec(Vec2{0.5, 0.5}, func(v interface{}) { t.Logf("find! %v", v)
// // t.Logf("find! %v", v) })
// //}) }
//}
// func TestTree2_Find_vec(t *testing.T) {
//func TestTree2_FindVec(t *testing.T) { type Vec2d = Vec2[float64]
// aabbs := []AABB[Vec2[float64]]{ type AABBVec2d = AABB[float64, Vec2d]
// {Upper: Vec2[float64]{2, 2}, Lower: Vec2[float64]{-1, -1}}, type TreeAABBVec2di = Tree[float64, AABBVec2d, int]
// {Upper: Vec2[float64]{2, 1}, Lower: Vec2[float64]{-1, -2}},
// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{-2, -2}}, aabbs := []AABBVec2d{
// {Upper: Vec2[float64]{1, 2}, Lower: Vec2[float64]{-2, -1}}, {Upper: Vec2d{2, 2}, Lower: Vec2d{-1, -1}},
// } {Upper: Vec2d{2, 1}, Lower: Vec2d{-1, -2}},
// var bvh Tree[float64, AABB[Vec2[float64]], int] {Upper: Vec2d{1, 1}, Lower: Vec2d{-2, -2}},
// for i, aabb := range aabbs { {Upper: Vec2d{1, 2}, Lower: Vec2d{-2, -1}},
// bvh.Insert(aabb, i) }
// // visualize var bvh TreeAABBVec2di
// t.Log(bvh) for i, aabb := range aabbs {
// } bvh.Insert(aabb, i)
// //findVec := func(vec Vec2) (list []interface{}) { t.Log(bvh)
// // bvh.FindVec(vec, func(v interface{}) { list = append(list, v) }) }
// // return find := func(test func(bound AABBVec2d) bool) []int {
// //} var result []int
// //t.Log(findVec(Vec2{0, 0})) bvh.Find(test, func(i int) {
// //t.Log(findVec(Vec2{1.5, 0})) result = append(result, i)
// //t.Log(findVec(Vec2{1.5, 1.5})) })
// //t.Log(findVec(Vec2{-1.5, 0})) return result
// // }
// //findAABB := func(aabb AABB2) (list []interface{}) { t.Log(find(TouchPoint[Vec2d, AABBVec2d](Vec2d{0, 0})))
// // bvh.FindAABB(aabb, func(v interface{}) { list = append(list, v) }) t.Log(find(TouchPoint[Vec2d, AABBVec2d](Vec2d{1.5, 0})))
// // return t.Log(find(TouchPoint[Vec2d, AABBVec2d](Vec2d{1.5, 1.5})))
// //} t.Log(find(TouchPoint[Vec2d, AABBVec2d](Vec2d{-1.5, 0})))
// //t.Log(findAABB(AABB2{Upper: Vec2{1, 1}, Lower: Vec2{-1, -1}}))
// //t.Log(findAABB(AABB2{Upper: Vec2{3, 3}, Lower: Vec2{1.5, 1.5}})) t.Log(find(TouchBound[Vec2d, AABBVec2d](AABBVec2d{Upper: Vec2d{1, 1}, Lower: Vec2d{-1, -1}})))
// //t.Log(findAABB(AABB2{Upper: Vec2{-1.5, 0.5}, Lower: Vec2{-2.5, -0.5}})) t.Log(find(TouchBound[Vec2d, AABBVec2d](AABBVec2d{Upper: Vec2d{1, 1}, Lower: Vec2d{1.5, 1.5}})))
//} t.Log(find(TouchBound[Vec2d, AABBVec2d](AABBVec2d{Upper: Vec2d{-1.5, 0.5}, Lower: Vec2d{-2.5, -0.5}})))
// }
//func TestTree2_Insert_rotation(t *testing.T) {
// var bvh Tree[float64, AABB[Vec2[float64]], int] func BenchmarkTree_Insert(b *testing.B) {
// for i := 0; i < 5; i++ { type Vec2d = Vec2[float64]
// bvh.Insert(AABB[Vec2[float64]]{ type AABBVec2d = AABB[float64, Vec2d]
// Upper: Vec2[float64]{float64(i), float64(i)}, type TreeAABBVec2da = Tree[float64, AABBVec2d, any]
// Lower: Vec2[float64]{float64(0), float64(0)},
// }, i) const size = 25
// } // generate test cases
//} aabbs := make([]AABBVec2d, b.N)
// poses := make([]Vec2d, b.N)
//func BenchmarkTree2_Insert_random(b *testing.B) { for i := range aabbs {
// const size = 25 poses[i] = Vec2d{rand.Float64() * 1e4, rand.Float64() * 1e4}
// // generate test cases aabbs[i] = AABBVec2d{
// aabbs := make([]AABB[Vec2[float64]], b.N) Upper: Vec2d{poses[i][0] + size, poses[i][0] + size},
// poses := make([]Vec2[float64], b.N) Lower: Vec2d{poses[i][0] - size, poses[i][0] - size},
// for i := range aabbs { }
// poses[i] = Vec2[float64]{rand.Float64() * 1e4, rand.Float64() * 1e4} }
// aabbs[i] = AABB[Vec2[float64]]{ b.ResetTimer()
// Upper: Vec2[float64]{poses[i][0] + size, poses[i][0] + size},
// Lower: Vec2[float64]{poses[i][0] - size, poses[i][0] - size}, var bvh TreeAABBVec2da
// } for _, v := range aabbs {
// } bvh.Insert(v, nil)
// b.ResetTimer() }
// }
// var bvh Tree[float64, AABB[Vec2[float64]], any]
// for _, v := range aabbs { func BenchmarkTree2_Find_random(b *testing.B) {
// bvh.Insert(v, nil) type Vec2d = Vec2[float64]
// } type AABBVec2d = AABB[float64, Vec2d]
// //for _, v := range poses { type TreeAABBVec2da = Tree[float64, AABBVec2d, any]
// // bvh.FindVec(v, func(interface{}) {})
// //} const size = 25
//} // generate test cases
// aabbs := make([]AABBVec2d, b.N)
//func BenchmarkTree2_Insert_sorted1(b *testing.B) { poses := make([]Vec2d, b.N)
// // generate test cases for i := range aabbs {
// var bvh Tree[float64, AABB[Vec2[float64]], int] poses[i] = Vec2d{rand.Float64() * 1e4, rand.Float64() * 1e4}
// upper := Vec2[float64]{float64(b.N), float64(b.N)} aabbs[i] = AABBVec2d{
// for i := 0; i < b.N; i++ { Upper: Vec2d{poses[i][0] + size, poses[i][0] + size},
// bvh.Insert(AABB[Vec2[float64]]{ Lower: Vec2d{poses[i][0] - size, poses[i][0] - size},
// Upper: upper, }
// Lower: Vec2[float64]{float64(i), float64(i)}, }
// }, i) var bvh TreeAABBVec2da
// } for _, v := range aabbs {
//} bvh.Insert(v, nil)
// }
//func BenchmarkTree2_Insert_sorted2(b *testing.B) { b.ResetTimer()
// // generate test cases
// var bvh Tree[float64, AABB[Vec2[float64]], int] for _, v := range poses {
// for i := 0; i < b.N; i++ { bvh.Find(TouchPoint[Vec2d, AABBVec2d](v), func(v any) {})
// 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[float64, Vec2[float64]], b.N)
//func BenchmarkTree2_Delete_random(b *testing.B) { poses := make([]Vec2[float64], b.N)
// const size = 25 nodes := make([]*Node[float64, AABB[float64, Vec2[float64]], any], b.N)
// // generate test cases for i := range aabbs {
// aabbs := make([]AABB[Vec2[float64]], b.N) poses[i] = Vec2[float64]{rand.Float64() * 1e4, rand.Float64() * 1e4}
// poses := make([]Vec2[float64], b.N) aabbs[i] = AABB[float64, Vec2[float64]]{
// nodes := make([]*Node[AABB[Vec2[float64]], any], b.N) Upper: Vec2[float64]{poses[i][0] + size, poses[i][0] + size},
// for i := range aabbs { Lower: Vec2[float64]{poses[i][0] - size, poses[i][0] - size},
// 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}, b.ResetTimer()
// Lower: Vec2[float64]{poses[i][0] - size, poses[i][0] - size},
// } var bvh Tree[float64, AABB[float64, Vec2[float64]], any]
// } for i, v := range aabbs {
// b.ResetTimer() nodes[i] = bvh.Insert(v, nil)
// }
// var bvh Tree[float64, AABB[Vec2[float64]], any]
// for i, v := range aabbs { b.StopTimer()
// nodes[i] = bvh.Insert(v, nil) rand.Shuffle(b.N, func(i, j int) {
// } nodes[i], nodes[j] = nodes[j], nodes[i]
// })
// b.StopTimer() b.StartTimer()
// rand.Shuffle(b.N, func(i, j int) {
// nodes[i], nodes[j] = nodes[j], nodes[i] for _, v := range nodes {
// }) bvh.Delete(v)
// b.StartTimer() }
// }
// for _, v := range nodes {
// bvh.Delete(v)
// }
//}