342 lines
6.8 KiB
Go
342 lines
6.8 KiB
Go
package world
|
|
|
|
const nullNode int32 = -1
|
|
|
|
type treeNode struct {
|
|
box AABB
|
|
parent int32
|
|
child1 int32
|
|
child2 int32
|
|
height int32
|
|
data int32
|
|
}
|
|
|
|
func (n *treeNode) leaf() bool {
|
|
return n.child1 == nullNode
|
|
}
|
|
|
|
type tree struct {
|
|
nodes []treeNode
|
|
root int32
|
|
free int32
|
|
count int
|
|
stack []int32
|
|
}
|
|
|
|
func newTree(capacity int) tree {
|
|
t := tree{root: nullNode, free: nullNode, stack: make([]int32, 0, 256)}
|
|
t.nodes = make([]treeNode, 0, max(capacity, 16))
|
|
return t
|
|
}
|
|
|
|
func (t *tree) alloc() int32 {
|
|
if t.free != nullNode {
|
|
id := t.free
|
|
t.free = t.nodes[id].parent
|
|
t.nodes[id] = treeNode{parent: nullNode, child1: nullNode, child2: nullNode}
|
|
t.count++
|
|
return id
|
|
}
|
|
t.nodes = append(t.nodes, treeNode{parent: nullNode, child1: nullNode, child2: nullNode})
|
|
t.count++
|
|
return int32(len(t.nodes) - 1)
|
|
}
|
|
|
|
func (t *tree) release(id int32) {
|
|
t.nodes[id] = treeNode{parent: t.free, child1: nullNode, child2: nullNode, height: -1}
|
|
t.free = id
|
|
t.count--
|
|
}
|
|
|
|
func (t *tree) create(box AABB, data int32) int32 {
|
|
id := t.alloc()
|
|
n := &t.nodes[id]
|
|
n.box, n.data = box, data
|
|
t.insertLeaf(id)
|
|
return id
|
|
}
|
|
|
|
func (t *tree) destroy(id int32) {
|
|
t.removeLeaf(id)
|
|
t.release(id)
|
|
}
|
|
|
|
func (t *tree) move(id int32, box AABB) {
|
|
t.removeLeaf(id)
|
|
t.nodes[id].box = box
|
|
t.insertLeaf(id)
|
|
}
|
|
|
|
func (t *tree) fat(id int32) AABB {
|
|
return t.nodes[id].box
|
|
}
|
|
|
|
func (t *tree) data(id int32) int32 {
|
|
return t.nodes[id].data
|
|
}
|
|
|
|
func (t *tree) setData(id, data int32) {
|
|
t.nodes[id].data = data
|
|
}
|
|
|
|
func (t *tree) height() int32 {
|
|
if t.root == nullNode {
|
|
return 0
|
|
}
|
|
return t.nodes[t.root].height
|
|
}
|
|
|
|
func (t *tree) insertLeaf(leaf int32) {
|
|
if t.root == nullNode {
|
|
t.root = leaf
|
|
t.nodes[leaf].parent = nullNode
|
|
return
|
|
}
|
|
lb := t.nodes[leaf].box
|
|
index := t.root
|
|
for !t.nodes[index].leaf() {
|
|
n := &t.nodes[index]
|
|
c1, c2 := n.child1, n.child2
|
|
area := boxPerimeter(n.box)
|
|
combined := boxPerimeter(boxUnion(n.box, lb))
|
|
cost := 2 * combined
|
|
inherit := 2 * (combined - area)
|
|
descend := func(c int32) float32 {
|
|
cn := &t.nodes[c]
|
|
u := boxPerimeter(boxUnion(lb, cn.box))
|
|
if cn.leaf() {
|
|
return u + inherit
|
|
}
|
|
return u - boxPerimeter(cn.box) + inherit
|
|
}
|
|
cost1, cost2 := descend(c1), descend(c2)
|
|
if cost < cost1 && cost < cost2 {
|
|
break
|
|
}
|
|
if cost1 < cost2 {
|
|
index = c1
|
|
} else {
|
|
index = c2
|
|
}
|
|
}
|
|
sibling := index
|
|
oldParent := t.nodes[sibling].parent
|
|
newParent := t.alloc()
|
|
np := &t.nodes[newParent]
|
|
np.parent = oldParent
|
|
np.data = -1
|
|
np.box = boxUnion(lb, t.nodes[sibling].box)
|
|
np.height = t.nodes[sibling].height + 1
|
|
np.child1, np.child2 = sibling, leaf
|
|
if oldParent != nullNode {
|
|
op := &t.nodes[oldParent]
|
|
if op.child1 == sibling {
|
|
op.child1 = newParent
|
|
} else {
|
|
op.child2 = newParent
|
|
}
|
|
} else {
|
|
t.root = newParent
|
|
}
|
|
t.nodes[sibling].parent = newParent
|
|
t.nodes[leaf].parent = newParent
|
|
t.refit(t.nodes[leaf].parent)
|
|
}
|
|
|
|
func (t *tree) refit(index int32) {
|
|
for index != nullNode {
|
|
index = t.balance(index)
|
|
n := &t.nodes[index]
|
|
c1, c2 := &t.nodes[n.child1], &t.nodes[n.child2]
|
|
n.height = 1 + max(c1.height, c2.height)
|
|
n.box = boxUnion(c1.box, c2.box)
|
|
index = n.parent
|
|
}
|
|
}
|
|
|
|
func (t *tree) removeLeaf(leaf int32) {
|
|
if leaf == t.root {
|
|
t.root = nullNode
|
|
return
|
|
}
|
|
parent := t.nodes[leaf].parent
|
|
grand := t.nodes[parent].parent
|
|
sibling := t.nodes[parent].child1
|
|
if sibling == leaf {
|
|
sibling = t.nodes[parent].child2
|
|
}
|
|
if grand == nullNode {
|
|
t.root = sibling
|
|
t.nodes[sibling].parent = nullNode
|
|
t.release(parent)
|
|
return
|
|
}
|
|
g := &t.nodes[grand]
|
|
if g.child1 == parent {
|
|
g.child1 = sibling
|
|
} else {
|
|
g.child2 = sibling
|
|
}
|
|
t.nodes[sibling].parent = grand
|
|
t.release(parent)
|
|
t.refit(grand)
|
|
}
|
|
|
|
func (t *tree) balance(iA int32) int32 {
|
|
A := &t.nodes[iA]
|
|
if A.leaf() || A.height < 2 {
|
|
return iA
|
|
}
|
|
iB, iC := A.child1, A.child2
|
|
B, C := &t.nodes[iB], &t.nodes[iC]
|
|
bal := C.height - B.height
|
|
if bal > 1 {
|
|
iF, iG := C.child1, C.child2
|
|
F, G := &t.nodes[iF], &t.nodes[iG]
|
|
C.child1 = iA
|
|
C.parent = A.parent
|
|
A.parent = iC
|
|
t.relink(C.parent, iA, iC)
|
|
if F.height > G.height {
|
|
C.child2 = iF
|
|
A.child2 = iG
|
|
G.parent = iA
|
|
A.box = boxUnion(B.box, G.box)
|
|
C.box = boxUnion(A.box, F.box)
|
|
A.height = 1 + max(B.height, G.height)
|
|
C.height = 1 + max(A.height, F.height)
|
|
} else {
|
|
C.child2 = iG
|
|
A.child2 = iF
|
|
F.parent = iA
|
|
A.box = boxUnion(B.box, F.box)
|
|
C.box = boxUnion(A.box, G.box)
|
|
A.height = 1 + max(B.height, F.height)
|
|
C.height = 1 + max(A.height, G.height)
|
|
}
|
|
return iC
|
|
}
|
|
if bal < -1 {
|
|
iD, iE := B.child1, B.child2
|
|
D, E := &t.nodes[iD], &t.nodes[iE]
|
|
B.child1 = iA
|
|
B.parent = A.parent
|
|
A.parent = iB
|
|
t.relink(B.parent, iA, iB)
|
|
if D.height > E.height {
|
|
B.child2 = iD
|
|
A.child1 = iE
|
|
E.parent = iA
|
|
A.box = boxUnion(C.box, E.box)
|
|
B.box = boxUnion(A.box, D.box)
|
|
A.height = 1 + max(C.height, E.height)
|
|
B.height = 1 + max(A.height, D.height)
|
|
} else {
|
|
B.child2 = iE
|
|
A.child1 = iD
|
|
D.parent = iA
|
|
A.box = boxUnion(C.box, D.box)
|
|
B.box = boxUnion(A.box, E.box)
|
|
A.height = 1 + max(C.height, D.height)
|
|
B.height = 1 + max(A.height, E.height)
|
|
}
|
|
return iB
|
|
}
|
|
return iA
|
|
}
|
|
|
|
func (t *tree) relink(parent, from, to int32) {
|
|
if parent == nullNode {
|
|
t.root = to
|
|
return
|
|
}
|
|
p := &t.nodes[parent]
|
|
if p.child1 == from {
|
|
p.child1 = to
|
|
} else {
|
|
p.child2 = to
|
|
}
|
|
}
|
|
|
|
func (t *tree) query(box AABB, fn func(id int32) bool) {
|
|
if t.root == nullNode {
|
|
return
|
|
}
|
|
st := append(t.stack[:0], t.root)
|
|
for len(st) > 0 {
|
|
id := st[len(st)-1]
|
|
st = st[:len(st)-1]
|
|
n := &t.nodes[id]
|
|
if !boxOverlap(n.box, box) {
|
|
continue
|
|
}
|
|
if n.leaf() {
|
|
if !fn(id) {
|
|
break
|
|
}
|
|
continue
|
|
}
|
|
st = append(st, n.child1, n.child2)
|
|
}
|
|
t.stack = st[:0]
|
|
}
|
|
|
|
func (t *tree) raycast(p1, p2 Vec2, maxFraction float32, fn func(id int32, p1, p2 Vec2, maxFraction float32) float32) {
|
|
if t.root == nullNode {
|
|
return
|
|
}
|
|
r, l := normalizeLen(p2.Sub(p1))
|
|
if l == 0 {
|
|
return
|
|
}
|
|
v := crossSV(1, r)
|
|
av := abs2(v)
|
|
seg := func(f float32) AABB {
|
|
e := p1.Add(p2.Sub(p1).Scale(f))
|
|
return AABB{Min: min2(p1, e), Max: max2(p1, e)}
|
|
}
|
|
box := seg(maxFraction)
|
|
st := append(t.stack[:0], t.root)
|
|
for len(st) > 0 {
|
|
id := st[len(st)-1]
|
|
st = st[:len(st)-1]
|
|
n := &t.nodes[id]
|
|
if !boxOverlap(n.box, box) {
|
|
continue
|
|
}
|
|
c := n.box.Center()
|
|
h := n.box.Size().Scale(0.5)
|
|
if abs32(v.Dot(p1.Sub(c)))-av.Dot(h) > 0 {
|
|
continue
|
|
}
|
|
if n.leaf() {
|
|
value := fn(id, p1, p2, maxFraction)
|
|
if value == 0 {
|
|
break
|
|
}
|
|
if value > 0 {
|
|
maxFraction = value
|
|
box = seg(maxFraction)
|
|
}
|
|
continue
|
|
}
|
|
st = append(st, n.child1, n.child2)
|
|
}
|
|
t.stack = st[:0]
|
|
}
|
|
|
|
func abs32(f float32) float32 {
|
|
if f < 0 {
|
|
return -f
|
|
}
|
|
return f
|
|
}
|
|
|
|
func (t *tree) cloneInto(dst *tree) {
|
|
dst.nodes = append(dst.nodes[:0], t.nodes...)
|
|
dst.root, dst.free, dst.count = t.root, t.free, t.count
|
|
if dst.stack == nil {
|
|
dst.stack = make([]int32, 0, max(cap(t.stack), 256))
|
|
}
|
|
}
|