diff --git a/go.mod b/go.mod index 3b9546d..0e74410 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,9 @@ module github.com/Tnze/go-mc -go 1.16 +go 1.18 require ( github.com/google/uuid v1.1.1 github.com/iancoleman/strcase v0.1.3 -) + golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7 +) \ No newline at end of file diff --git a/go.sum b/go.sum index 72ce2f9..4ab0d58 100644 --- a/go.sum +++ b/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/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CTdBw= 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= diff --git a/server/internal/bvh/bound.go b/server/internal/bvh/bound.go new file mode 100644 index 0000000..85f7144 --- /dev/null +++ b/server/internal/bvh/bound.go @@ -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) } diff --git a/server/internal/bvh/bound_test.go b/server/internal/bvh/bound_test.go new file mode 100644 index 0000000..a77737c --- /dev/null +++ b/server/internal/bvh/bound_test.go @@ -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") + } +} diff --git a/server/internal/bvh/bvh.go b/server/internal/bvh/bvh.go index 577855c..eb7d396 100644 --- a/server/internal/bvh/bvh.go +++ b/server/internal/bvh/bvh.go @@ -1,158 +1,211 @@ package bvh -import ( - "container/heap" - "math" -) - -type Vec2 [2]float64 - -func (v Vec2) Add(other Vec2) Vec2 { return Vec2{v[0] + other[0], v[1] + other[1]} } -func (v Vec2) Sub(other Vec2) Vec2 { return Vec2{v[0] - other[0], v[1] - other[1]} } -func (v Vec2) Max(other Vec2) Vec2 { return Vec2{math.Max(v[0], other[0]), math.Max(v[1], other[1])} } -func (v Vec2) Min(other Vec2) Vec2 { return Vec2{math.Min(v[0], other[0]), math.Min(v[1], other[1])} } - -type AABB2 struct{ Upper, Lower Vec2 } - -func (aabb AABB2) WithIn(point Vec2) bool { - return aabb.Lower[0] < point[0] && point[0] < aabb.Upper[0] && - aabb.Lower[1] < point[1] && point[1] < aabb.Upper[1] -} - -func (aabb AABB2) Union(other AABB2) AABB2 { - return AABB2{ - Upper: aabb.Upper.Max(other.Upper), - Lower: aabb.Lower.Min(other.Lower), - } -} - -func (aabb AABB2) Surface() float64 { - d := aabb.Upper.Sub(aabb.Lower) - return 2 * (d[0] + d[1]) -} - -type Node2 struct { - box AABB2 - Value interface{} - parent *Node2 - children [2]*Node2 - isLeaf bool -} - -func (n *Node2) findAnotherChild(not *Node2) *Node2 { - if n.isLeaf { - return nil - } else if n.children[0] == not { - return n.children[1] - } else if n.children[1] == not { - return n.children[0] - } - panic("unreachable, please make sure the 'not' is the n's child") -} - -type Tree2 struct { - root *Node2 -} - -func (t *Tree2) Insert(leaf AABB2) (n *Node2) { - n = &Node2{ - box: leaf, - parent: nil, - children: [2]*Node2{}, - isLeaf: true, - } - if t.root == nil { - t.root = n - return - } - - // Stage 1: find the best sibling for the new leaf - sibling := t.root - bestCost := t.root.box.Union(leaf).Surface() - parentTo := &t.root // the parent's children pointer which point to the sibling - queue := searchHeap{searchItem{pointer: t.root, parentTo: &t.root}} - - leafCost := leaf.Surface() - for len(queue) > 0 { - p := heap.Pop(&queue).(searchItem) - // determine if node p has the best cost - mergeSurface := p.pointer.box.Union(leaf).Surface() - deltaCost := mergeSurface - p.pointer.box.Surface() - cost := p.inheritedCost + mergeSurface - if cost < bestCost { - bestCost = cost - sibling = p.pointer - parentTo = p.parentTo - } - // determine if it is worthwhile to explore the children of node p. - inheritedCost := p.inheritedCost + deltaCost // lower bound - if inheritedCost+leafCost < bestCost { - if p.pointer.children[0] != nil { - heap.Push(&queue, searchItem{ - pointer: p.pointer.children[0], - parentTo: &p.pointer.children[0], - inheritedCost: inheritedCost, - }) - } - if p.pointer.children[1] != nil { - heap.Push(&queue, searchItem{ - pointer: p.pointer.children[1], - parentTo: &p.pointer.children[1], - inheritedCost: inheritedCost, - }) - } - } - } - - // Stage 2: create a new parent - *parentTo = &Node2{ - box: sibling.box.Union(leaf), // we will calculate in Stage3 - parent: sibling.parent, - children: [2]*Node2{sibling, n}, - isLeaf: false, - } - n.parent = *parentTo - sibling.parent = *parentTo - - // Stage 3: walk back up the tree refitting AABBs - for p := *parentTo; p.parent != nil; p = p.parent { - p.box = p.children[0].box.Union(p.children[1].box) - //TODO: t.rotate(p) - } - return -} - -func (t *Tree2) Find(point Vec2, f func(*Node2)) { - t.root.lookup(point, f) -} - -func (n *Node2) lookup(point Vec2, f func(node2 *Node2)) { - if n == nil || !n.box.WithIn(point) { - return - } - if n.isLeaf { - f(n) - } else { - n.children[0].lookup(point, f) - n.children[1].lookup(point, f) - } -} - -type searchHeap []searchItem -type searchItem struct { - pointer *Node2 - parentTo **Node2 - inheritedCost float64 -} - -func (h searchHeap) Len() int { return len(h) } -func (h searchHeap) Less(i, j int) bool { return h[i].inheritedCost < h[j].inheritedCost } -func (h searchHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } -func (h *searchHeap) Push(x interface{}) { *h = append(*h, x.(searchItem)) } -func (h *searchHeap) Pop() interface{} { - old := *h - n := len(old) - x := old[n-1] - *h = old[0 : n-1] - return x -} +// +//import ( +// "container/heap" +// "fmt" +// "golang.org/x/exp/constraints" +//) +// +//type Node[Vec constraints.Signed | constraints.Float, B interface { +// WithIn(Vec) bool +// Union(B) B +// Surface() float64 +//}, V any] struct { +// box B +// Value V +// parent *Node[Vec, B, V] +// children [2]*Node[Vec, B, V] +// isLeaf bool +//} +// +//func (n *Node[Vec, B, V]) findAnotherChild(not *Node[Vec, B, V]) *Node[Vec, B, V] { +// if n.children[0] == not { +// return n.children[1] +// } else if n.children[1] == not { +// return n.children[0] +// } +// panic("unreachable, please make sure the 'not' is the n's child") +//} +// +//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] +// } else if n.children[1] == child { +// return &n.children[1] +// } +// panic("unreachable, please make sure the 'not' is the n's child") +//} +// +//type Tree[I constraints.Signed | constraints.Float, B interface { +// Union(B) B +// Surface() I +//}, V any] struct { +// root *Node[I, B, V] +//} +// +//func (t *Tree[Vec, B, V]) Insert(leaf B, value V) (n *Node[Vec, B, V]) { +// n = &Node[Vec, B, V]{ +// box: leaf, +// Value: value, +// parent: nil, +// children: [2]*Node[Vec, B, V]{}, +// isLeaf: true, +// } +// if t.root == nil { +// t.root = n +// return +// } +// +// // Stage 1: find the best sibling for the new leaf +// sibling := t.root +// bestCost := t.root.box.Union(leaf).Surface() +// parentTo := &t.root // the parent's children pointer which point to the sibling +// queue := searchHeap[Node[Vec, B, V]]{searchItem[Node[Vec, B, V]]{pointer: t.root, parentTo: &t.root}} +// +// leafCost := leaf.Surface() +// for queue.Len() > 0 { +// p := heap.Pop(&queue).(searchItem[Node[Vec, B, V]]) +// // determine if node p has the best cost +// mergeSurface := p.pointer.box.Union(leaf).Surface() +// deltaCost := mergeSurface - p.pointer.box.Surface() +// cost := p.inheritedCost + mergeSurface +// if cost <= bestCost { +// bestCost = cost +// sibling = p.pointer +// parentTo = p.parentTo +// } +// // determine if it is worthwhile to explore the children of node p. +// inheritedCost := p.inheritedCost + deltaCost // lower bound +// if !p.pointer.isLeaf && inheritedCost+leafCost < bestCost { +// heap.Push(&queue, searchItem[Node[Vec, B, V]]{ +// pointer: p.pointer.children[0], +// parentTo: &p.pointer.children[0], +// inheritedCost: inheritedCost, +// }) +// heap.Push(&queue, searchItem[Node[Vec, B, V]]{ +// pointer: p.pointer.children[1], +// parentTo: &p.pointer.children[1], +// inheritedCost: inheritedCost, +// }) +// } +// } +// +// // Stage 2: create a new parent +// *parentTo = &Node[Vec, B, V]{ +// box: sibling.box.Union(leaf), // we will calculate in Stage3 +// parent: sibling.parent, +// children: [2]*Node[Vec, B, V]{sibling, n}, +// isLeaf: false, +// } +// 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) +// t.rotate(p) +// } +// return +//} +// +//func (t *Tree[Vec, B, V]) Delete(n *Node[Vec, B, V]) interface{} { +// if n.parent == nil { +// // n is the root +// t.root = nil +// return n.Value +// } +// sibling := n.parent.findAnotherChild(n) +// grand := n.parent.parent +// if grand == nil { +// // n's parent is root +// t.root = sibling +// sibling.parent = nil +// } else { +// p := grand.findChildPointer(n.parent) +// *p = sibling +// sibling.parent = grand +// for p := sibling.parent; p.parent != nil; p = p.parent { +// p.box = p.children[0].box.Union(p.children[1].box) +// t.rotate(p) +// } +// } +// return n.Value +//} +// +//func (t *Tree[Vec, B, V]) rotate(n *Node[Vec, B, V]) { +// if n.isLeaf || n.parent == nil { +// return +// } +// // trying to swap n's sibling and children +// sibling := n.parent.findAnotherChild(n) +// current := n.box.Surface() +// if n.children[1].box.Union(sibling.box).Surface() < current { +// // swap n.children[0] and sibling +// 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 { +// // 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 lookupPoint[B interface { +//// Union(B) B +//// Surface() float64 +////}, 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]) +// } +//} diff --git a/server/internal/bvh/bvh_test.go b/server/internal/bvh/bvh_test.go index 813c56d..5193237 100644 --- a/server/internal/bvh/bvh_test.go +++ b/server/internal/bvh/bvh_test.go @@ -1,107 +1,148 @@ package bvh -import ( - "fmt" - "math/rand" - "strings" - "testing" -) - -func TestTree2_Insert(t *testing.T) { - aabbs := []AABB2{ - {Upper: Vec2{1, 1}, Lower: Vec2{0, 0}}, - {Upper: Vec2{2, 1}, Lower: Vec2{1, 0}}, - {Upper: Vec2{11, 1}, Lower: Vec2{10, 0}}, - {Upper: Vec2{12, 1}, Lower: Vec2{11, 0}}, - {Upper: Vec2{101, 1}, Lower: Vec2{100, 0}}, - {Upper: Vec2{102, 1}, Lower: Vec2{101, 0}}, - {Upper: Vec2{111, 1}, Lower: Vec2{110, 0}}, - {Upper: Vec2{112, 1}, Lower: Vec2{111, 0}}, - {Upper: Vec2{1, 1}, Lower: Vec2{-1, -1}}, - } - var bvh Tree2 - for _, aabb := range aabbs { - bvh.Insert(aabb) - // visualize - var sb strings.Builder - toString(&sb, bvh.root) - t.Log(sb.String()) - } - bvh.Find(Vec2{0.5, 0.5}, func(n *Node2) { - t.Logf("find! %v", n.box) - }) -} - -func TestTree2_Insert2_notLinkedTable(t *testing.T) { - const items = 1000 - var bvh Tree2 - for i := 0; i < items; i++ { - pos := Vec2{0, float64(i)} - bvh.Insert(AABB2{ - Upper: Vec2{pos[0] + 1, pos[1] + 1}, - Lower: Vec2{pos[0], pos[1]}, - }) - } - //calc depth - if depth := lookupDepth(bvh.root); depth > items/2 { - t.Errorf("the bvh is unbalanced: depth %d", depth) - } else { - t.Logf("the depth of bvh with %d element is %d", items, depth) - } -} - -func toString(sb *strings.Builder, n *Node2) { - if n.isLeaf { - _, _ = fmt.Fprintf(sb, "(%v,%v)", n.box.Upper[1], n.box.Upper[0]) - return - } - sb.WriteByte('{') - v1 := n.children[0] - if v1 != nil { - toString(sb, v1) - } - v2 := n.children[1] - if v2 != nil { - if v1 != nil { - sb.WriteString(", ") - } - toString(sb, v2) - } - sb.WriteByte('}') -} - -func lookupDepth(n *Node2) int { - depth := 0 - for _, child := range n.children { - if child != nil { - subdepth := lookupDepth(child) - if subdepth > depth { - depth = subdepth - } - } - } - return depth + 1 // add itself -} - -func BenchmarkTree2_Insert(b *testing.B) { - const size = 25 - // generate test cases - aabbs := make([]AABB2, b.N) - poses := make([]Vec2, b.N) - for i := range aabbs { - poses[i] = Vec2{rand.Float64() * 1e4, rand.Float64() * 1e4} - aabbs[i] = AABB2{ - Upper: Vec2{poses[i][0] + size, poses[i][0] + size}, - Lower: Vec2{poses[i][0] - size, poses[i][0] - size}, - } - } - b.ResetTimer() - - var bvh Tree2 - for _, v := range aabbs { - bvh.Insert(v) - } - for _, v := range poses { - bvh.Find(v, func(n *Node2) {}) - } -} +// +//import ( +// "math/rand" +// "testing" +//) +// +//func TestTree2_Insert(t *testing.T) { +// aabbs := []AABB[Vec2[float64]]{ +// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{0, 0}}, +// {Upper: Vec2[float64]{2, 1}, Lower: Vec2[float64]{1, 0}}, +// {Upper: Vec2[float64]{11, 1}, Lower: Vec2[float64]{10, 0}}, +// {Upper: Vec2[float64]{12, 1}, Lower: Vec2[float64]{11, 0}}, +// {Upper: Vec2[float64]{101, 1}, Lower: Vec2[float64]{100, 0}}, +// {Upper: Vec2[float64]{102, 1}, Lower: Vec2[float64]{101, 0}}, +// {Upper: Vec2[float64]{111, 1}, Lower: Vec2[float64]{110, 0}}, +// {Upper: Vec2[float64]{112, 1}, Lower: Vec2[float64]{111, 0}}, +// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{-1, -1}}, +// } +// var bvh Tree[float64, AABB[Vec2[float64]], int] +// for i, aabb := range aabbs { +// bvh.Insert(aabb, i) +// // visualize +// t.Log(bvh) +// } +// //bvh.FindVec(Vec2{0.5, 0.5}, func(v interface{}) { +// // t.Logf("find! %v", v) +// //}) +//} +// +//func TestTree2_FindVec(t *testing.T) { +// aabbs := []AABB[Vec2[float64]]{ +// {Upper: Vec2[float64]{2, 2}, Lower: Vec2[float64]{-1, -1}}, +// {Upper: Vec2[float64]{2, 1}, Lower: Vec2[float64]{-1, -2}}, +// {Upper: Vec2[float64]{1, 1}, Lower: Vec2[float64]{-2, -2}}, +// {Upper: Vec2[float64]{1, 2}, Lower: Vec2[float64]{-2, -1}}, +// } +// var bvh Tree[float64, AABB[Vec2[float64]], int] +// for i, aabb := range aabbs { +// bvh.Insert(aabb, i) +// // visualize +// t.Log(bvh) +// } +// //findVec := func(vec Vec2) (list []interface{}) { +// // bvh.FindVec(vec, func(v interface{}) { list = append(list, v) }) +// // return +// //} +// //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})) +// // +// //findAABB := func(aabb AABB2) (list []interface{}) { +// // bvh.FindAABB(aabb, func(v interface{}) { list = append(list, v) }) +// // return +// //} +// //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(findAABB(AABB2{Upper: Vec2{-1.5, 0.5}, Lower: Vec2{-2.5, -0.5}})) +//} +// +//func TestTree2_Insert_rotation(t *testing.T) { +// var bvh Tree[float64, AABB[Vec2[float64]], int] +// for i := 0; i < 5; i++ { +// bvh.Insert(AABB[Vec2[float64]]{ +// Upper: Vec2[float64]{float64(i), float64(i)}, +// Lower: Vec2[float64]{float64(0), float64(0)}, +// }, i) +// } +//} +// +//func BenchmarkTree2_Insert_random(b *testing.B) { +// const size = 25 +// // generate test cases +// aabbs := make([]AABB[Vec2[float64]], b.N) +// poses := make([]Vec2[float64], 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 _, v := range aabbs { +// bvh.Insert(v, nil) +// } +// //for _, v := range poses { +// // bvh.FindVec(v, func(interface{}) {}) +// //} +//} +// +//func BenchmarkTree2_Insert_sorted1(b *testing.B) { +// // generate test cases +// var bvh Tree[float64, AABB[Vec2[float64]], int] +// upper := Vec2[float64]{float64(b.N), float64(b.N)} +// for i := 0; i < b.N; i++ { +// bvh.Insert(AABB[Vec2[float64]]{ +// Upper: upper, +// Lower: Vec2[float64]{float64(i), float64(i)}, +// }, i) +// } +//} +// +//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) +// } +//} diff --git a/server/internal/bvh/vector.go b/server/internal/bvh/vector.go new file mode 100644 index 0000000..593ea4e --- /dev/null +++ b/server/internal/bvh/vector.go @@ -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)) +}