14014 lines
432 KiB
Go
14014 lines
432 KiB
Go
// generated; DO NOT EDIT!
|
|
|
|
package rtree
|
|
|
|
import "math"
|
|
|
|
type Iterator func(item Item) bool
|
|
type Item interface {
|
|
Rect(ctx interface{}) (min []float64, max []float64)
|
|
}
|
|
|
|
type RTree struct {
|
|
ctx interface{}
|
|
tr1 *d1RTree
|
|
tr2 *d2RTree
|
|
tr3 *d3RTree
|
|
tr4 *d4RTree
|
|
tr5 *d5RTree
|
|
tr6 *d6RTree
|
|
tr7 *d7RTree
|
|
tr8 *d8RTree
|
|
tr9 *d9RTree
|
|
tr10 *d10RTree
|
|
tr11 *d11RTree
|
|
tr12 *d12RTree
|
|
tr13 *d13RTree
|
|
tr14 *d14RTree
|
|
tr15 *d15RTree
|
|
tr16 *d16RTree
|
|
tr17 *d17RTree
|
|
tr18 *d18RTree
|
|
tr19 *d19RTree
|
|
tr20 *d20RTree
|
|
}
|
|
|
|
func New(ctx interface{}) *RTree {
|
|
return &RTree{
|
|
ctx: ctx,
|
|
tr1: d1New(),
|
|
tr2: d2New(),
|
|
tr3: d3New(),
|
|
tr4: d4New(),
|
|
tr5: d5New(),
|
|
tr6: d6New(),
|
|
tr7: d7New(),
|
|
tr8: d8New(),
|
|
tr9: d9New(),
|
|
tr10: d10New(),
|
|
tr11: d11New(),
|
|
tr12: d12New(),
|
|
tr13: d13New(),
|
|
tr14: d14New(),
|
|
tr15: d15New(),
|
|
tr16: d16New(),
|
|
tr17: d17New(),
|
|
tr18: d18New(),
|
|
tr19: d19New(),
|
|
tr20: d20New(),
|
|
}
|
|
}
|
|
|
|
func (tr *RTree) Insert(item Item) {
|
|
if item == nil {
|
|
panic("nil item being added to RTree")
|
|
}
|
|
min, max := item.Rect(tr.ctx)
|
|
if len(min) != len(max) {
|
|
return // just return
|
|
panic("invalid item rectangle")
|
|
}
|
|
switch len(min) {
|
|
default:
|
|
return // just return
|
|
panic("invalid dimension")
|
|
case 1:
|
|
var amin, amax [1]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr1.Insert(amin, amax, item)
|
|
case 2:
|
|
var amin, amax [2]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr2.Insert(amin, amax, item)
|
|
case 3:
|
|
var amin, amax [3]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr3.Insert(amin, amax, item)
|
|
case 4:
|
|
var amin, amax [4]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr4.Insert(amin, amax, item)
|
|
case 5:
|
|
var amin, amax [5]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr5.Insert(amin, amax, item)
|
|
case 6:
|
|
var amin, amax [6]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr6.Insert(amin, amax, item)
|
|
case 7:
|
|
var amin, amax [7]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr7.Insert(amin, amax, item)
|
|
case 8:
|
|
var amin, amax [8]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr8.Insert(amin, amax, item)
|
|
case 9:
|
|
var amin, amax [9]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr9.Insert(amin, amax, item)
|
|
case 10:
|
|
var amin, amax [10]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr10.Insert(amin, amax, item)
|
|
case 11:
|
|
var amin, amax [11]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr11.Insert(amin, amax, item)
|
|
case 12:
|
|
var amin, amax [12]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr12.Insert(amin, amax, item)
|
|
case 13:
|
|
var amin, amax [13]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr13.Insert(amin, amax, item)
|
|
case 14:
|
|
var amin, amax [14]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr14.Insert(amin, amax, item)
|
|
case 15:
|
|
var amin, amax [15]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr15.Insert(amin, amax, item)
|
|
case 16:
|
|
var amin, amax [16]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr16.Insert(amin, amax, item)
|
|
case 17:
|
|
var amin, amax [17]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr17.Insert(amin, amax, item)
|
|
case 18:
|
|
var amin, amax [18]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr18.Insert(amin, amax, item)
|
|
case 19:
|
|
var amin, amax [19]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr19.Insert(amin, amax, item)
|
|
case 20:
|
|
var amin, amax [20]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr20.Insert(amin, amax, item)
|
|
}
|
|
}
|
|
|
|
func (tr *RTree) Remove(item Item) {
|
|
if item == nil {
|
|
panic("nil item being added to RTree")
|
|
}
|
|
min, max := item.Rect(tr.ctx)
|
|
if len(min) != len(max) {
|
|
return // just return
|
|
panic("invalid item rectangle")
|
|
}
|
|
switch len(min) {
|
|
default:
|
|
return // just return
|
|
panic("invalid dimension")
|
|
case 1:
|
|
var amin, amax [1]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr1.Remove(amin, amax, item)
|
|
case 2:
|
|
var amin, amax [2]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr2.Remove(amin, amax, item)
|
|
case 3:
|
|
var amin, amax [3]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr3.Remove(amin, amax, item)
|
|
case 4:
|
|
var amin, amax [4]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr4.Remove(amin, amax, item)
|
|
case 5:
|
|
var amin, amax [5]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr5.Remove(amin, amax, item)
|
|
case 6:
|
|
var amin, amax [6]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr6.Remove(amin, amax, item)
|
|
case 7:
|
|
var amin, amax [7]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr7.Remove(amin, amax, item)
|
|
case 8:
|
|
var amin, amax [8]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr8.Remove(amin, amax, item)
|
|
case 9:
|
|
var amin, amax [9]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr9.Remove(amin, amax, item)
|
|
case 10:
|
|
var amin, amax [10]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr10.Remove(amin, amax, item)
|
|
case 11:
|
|
var amin, amax [11]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr11.Remove(amin, amax, item)
|
|
case 12:
|
|
var amin, amax [12]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr12.Remove(amin, amax, item)
|
|
case 13:
|
|
var amin, amax [13]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr13.Remove(amin, amax, item)
|
|
case 14:
|
|
var amin, amax [14]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr14.Remove(amin, amax, item)
|
|
case 15:
|
|
var amin, amax [15]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr15.Remove(amin, amax, item)
|
|
case 16:
|
|
var amin, amax [16]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr16.Remove(amin, amax, item)
|
|
case 17:
|
|
var amin, amax [17]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr17.Remove(amin, amax, item)
|
|
case 18:
|
|
var amin, amax [18]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr18.Remove(amin, amax, item)
|
|
case 19:
|
|
var amin, amax [19]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr19.Remove(amin, amax, item)
|
|
case 20:
|
|
var amin, amax [20]float64
|
|
for i := 0; i < len(min); i++ {
|
|
amin[i], amax[i] = min[i], max[i]
|
|
}
|
|
tr.tr20.Remove(amin, amax, item)
|
|
}
|
|
}
|
|
func (tr *RTree) Reset() {
|
|
tr.tr1 = d1New()
|
|
tr.tr2 = d2New()
|
|
tr.tr3 = d3New()
|
|
tr.tr4 = d4New()
|
|
tr.tr5 = d5New()
|
|
tr.tr6 = d6New()
|
|
tr.tr7 = d7New()
|
|
tr.tr8 = d8New()
|
|
tr.tr9 = d9New()
|
|
tr.tr10 = d10New()
|
|
tr.tr11 = d11New()
|
|
tr.tr12 = d12New()
|
|
tr.tr13 = d13New()
|
|
tr.tr14 = d14New()
|
|
tr.tr15 = d15New()
|
|
tr.tr16 = d16New()
|
|
tr.tr17 = d17New()
|
|
tr.tr18 = d18New()
|
|
tr.tr19 = d19New()
|
|
tr.tr20 = d20New()
|
|
}
|
|
func (tr *RTree) Count() int {
|
|
count := 0
|
|
count += tr.tr1.Count()
|
|
count += tr.tr2.Count()
|
|
count += tr.tr3.Count()
|
|
count += tr.tr4.Count()
|
|
count += tr.tr5.Count()
|
|
count += tr.tr6.Count()
|
|
count += tr.tr7.Count()
|
|
count += tr.tr8.Count()
|
|
count += tr.tr9.Count()
|
|
count += tr.tr10.Count()
|
|
count += tr.tr11.Count()
|
|
count += tr.tr12.Count()
|
|
count += tr.tr13.Count()
|
|
count += tr.tr14.Count()
|
|
count += tr.tr15.Count()
|
|
count += tr.tr16.Count()
|
|
count += tr.tr17.Count()
|
|
count += tr.tr18.Count()
|
|
count += tr.tr19.Count()
|
|
count += tr.tr20.Count()
|
|
return count
|
|
}
|
|
func (tr *RTree) Search(bounds Item, iter Iterator) {
|
|
if bounds == nil {
|
|
panic("nil bounds being used for search")
|
|
}
|
|
min, max := bounds.Rect(tr.ctx)
|
|
if len(min) != len(max) {
|
|
return // just return
|
|
panic("invalid item rectangle")
|
|
}
|
|
switch len(min) {
|
|
default:
|
|
return // just return
|
|
panic("invalid dimension")
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
case 19:
|
|
case 20:
|
|
}
|
|
if !tr.search1(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search2(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search3(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search4(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search5(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search6(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search7(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search8(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search9(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search10(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search11(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search12(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search13(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search14(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search15(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search16(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search17(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search18(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search19(min, max, iter) {
|
|
return
|
|
}
|
|
if !tr.search20(min, max, iter) {
|
|
return
|
|
}
|
|
}
|
|
|
|
func (tr *RTree) search1(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [1]float64
|
|
for i := 0; i < 1; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr1.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search2(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [2]float64
|
|
for i := 0; i < 2; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr2.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search3(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [3]float64
|
|
for i := 0; i < 3; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr3.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search4(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [4]float64
|
|
for i := 0; i < 4; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr4.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search5(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [5]float64
|
|
for i := 0; i < 5; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr5.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search6(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [6]float64
|
|
for i := 0; i < 6; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr6.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search7(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [7]float64
|
|
for i := 0; i < 7; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr7.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search8(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [8]float64
|
|
for i := 0; i < 8; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr8.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search9(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [9]float64
|
|
for i := 0; i < 9; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr9.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search10(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [10]float64
|
|
for i := 0; i < 10; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr10.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search11(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [11]float64
|
|
for i := 0; i < 11; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr11.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search12(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [12]float64
|
|
for i := 0; i < 12; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr12.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search13(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [13]float64
|
|
for i := 0; i < 13; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr13.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search14(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [14]float64
|
|
for i := 0; i < 14; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr14.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search15(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [15]float64
|
|
for i := 0; i < 15; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr15.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search16(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [16]float64
|
|
for i := 0; i < 16; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr16.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search17(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [17]float64
|
|
for i := 0; i < 17; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr17.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search18(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [18]float64
|
|
for i := 0; i < 18; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr18.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search19(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [19]float64
|
|
for i := 0; i < 19; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr19.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func (tr *RTree) search20(min, max []float64, iter Iterator) bool {
|
|
var amin, amax [20]float64
|
|
for i := 0; i < 20; i++ {
|
|
if i < len(min) {
|
|
amin[i] = min[i]
|
|
amax[i] = max[i]
|
|
} else {
|
|
amin[i] = math.Inf(-1)
|
|
amax[i] = math.Inf(+1)
|
|
}
|
|
}
|
|
ended := false
|
|
tr.tr20.Search(amin, amax, func(dataID interface{}) bool {
|
|
if !iter(dataID.(Item)) {
|
|
ended = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return !ended
|
|
}
|
|
|
|
func d1fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d1fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d1numDims = 1
|
|
d1maxNodes = 8
|
|
d1minNodes = d1maxNodes / 2
|
|
d1useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d1unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d1numDims]
|
|
|
|
type d1RTree struct {
|
|
root *d1nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d1rectT struct {
|
|
min [d1numDims]float64 ///< Min dimensions of bounding box
|
|
max [d1numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d1branchT struct {
|
|
rect d1rectT ///< Bounds
|
|
child *d1nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d1nodeT for each branch level
|
|
type d1nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d1maxNodes]d1branchT ///< Branch
|
|
}
|
|
|
|
func (node *d1nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d1nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d1listNodeT struct {
|
|
next *d1listNodeT ///< Next in list
|
|
node *d1nodeT ///< Node
|
|
}
|
|
|
|
const d1notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d1partitionVarsT struct {
|
|
partition [d1maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d1rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d1maxNodes + 1]d1branchT
|
|
branchCount int
|
|
coverSplit d1rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d1New() *d1RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d1RTree{
|
|
root: &d1nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d1RTree) Insert(min, max [d1numDims]float64, dataId interface{}) {
|
|
var branch d1branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d1numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d1insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d1RTree) Remove(min, max [d1numDims]float64, dataId interface{}) {
|
|
var rect d1rectT
|
|
for axis := 0; axis < d1numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d1removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d1search rectangle
|
|
/// \param a_min Min of d1search bounding rect
|
|
/// \param a_max Max of d1search bounding rect
|
|
/// \param a_searchResult d1search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d1RTree) Search(min, max [d1numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d1rectT
|
|
for axis := 0; axis < d1numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d1search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d1RTree) Count() int {
|
|
var count int
|
|
d1countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d1RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d1nodeT{}
|
|
}
|
|
|
|
func d1countRec(node *d1nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d1countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d1insertRectRec(branch *d1branchT, node *d1nodeT, newNode **d1nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d1nodeT
|
|
//var newBranch d1branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d1pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d1insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d1combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d1nodeCover(node.branch[index].child)
|
|
var newBranch d1branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d1nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d1addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d1addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d1insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d1insertRect(branch *d1branchT, root **d1nodeT, level int) bool {
|
|
var newNode *d1nodeT
|
|
|
|
if d1insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d1nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d1branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d1nodeCover(*root)
|
|
newBranch.child = *root
|
|
d1addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d1nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d1addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d1nodeCover(node *d1nodeT) d1rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d1combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d1addBranch(branch *d1branchT, node *d1nodeT, newNode **d1nodeT) bool {
|
|
if node.count < d1maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d1splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d1disconnectBranch(node *d1nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d1pickBranch(rect *d1rectT, node *d1nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d1rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d1calcRectVolume(curRect)
|
|
tempRect = d1combineRect(rect, curRect)
|
|
increase = d1calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d1combineRect(rectA, rectB *d1rectT) d1rectT {
|
|
var newRect d1rectT
|
|
|
|
for index := 0; index < d1numDims; index++ {
|
|
newRect.min[index] = d1fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d1fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d1splitNode(node *d1nodeT, branch *d1branchT, newNode **d1nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d1partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d1getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d1choosePartition(parVars, d1minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d1nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d1loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d1rectVolume(rect *d1rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d1numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d1rectT
|
|
func d1rectSphericalVolume(rect *d1rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d1numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d1numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d1unitSphereVolume)
|
|
} else if d1numDims == 4 {
|
|
return (radius * radius * radius * radius * d1unitSphereVolume)
|
|
} else if d1numDims == 3 {
|
|
return (radius * radius * radius * d1unitSphereVolume)
|
|
} else if d1numDims == 2 {
|
|
return (radius * radius * d1unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d1numDims) * d1unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d1calcRectVolume(rect *d1rectT) float64 {
|
|
if d1useSphericalVolume {
|
|
return d1rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d1rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d1getBranches(node *d1nodeT, branch *d1branchT, parVars *d1partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d1maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d1maxNodes] = *branch
|
|
parVars.branchCount = d1maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d1maxNodes+1; index++ {
|
|
parVars.coverSplit = d1combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d1calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d1choosePartition(parVars *d1partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d1initParVars(parVars, parVars.branchCount, minFill)
|
|
d1pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d1notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d1combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d1combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d1calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d1calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d1classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d1notTaken == parVars.partition[index] {
|
|
d1classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d1loadNodes(nodeA, nodeB *d1nodeT, parVars *d1partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d1nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d1addBranch here will not cause a node split.
|
|
d1addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d1partitionVarsT structure.
|
|
func d1initParVars(parVars *d1partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d1notTaken
|
|
}
|
|
}
|
|
|
|
func d1pickSeeds(parVars *d1partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d1maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d1calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d1combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d1calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d1classify(seed0, 0, parVars)
|
|
d1classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d1classify(index, group int, parVars *d1partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d1combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d1calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d1rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d1removeRect provides for eliminating the root.
|
|
func d1removeRect(rect *d1rectT, id interface{}, root **d1nodeT) bool {
|
|
var reInsertList *d1listNodeT
|
|
|
|
if !d1removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d1insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d1removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d1removeRectRec(rect *d1rectT, id interface{}, node *d1nodeT, listNode **d1listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d1overlap(*rect, node.branch[index].rect) {
|
|
if !d1removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d1minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d1nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d1reInsert(node.branch[index].child, listNode)
|
|
d1disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d1disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d1overlap.
|
|
func d1overlap(rectA, rectB d1rectT) bool {
|
|
for index := 0; index < d1numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d1reInsert(node *d1nodeT, listNode **d1listNodeT) {
|
|
newListNode := &d1listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d1search in an index tree or subtree for all data retangles that d1overlap the argument rectangle.
|
|
func d1search(node *d1nodeT, rect d1rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d1overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d1search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d1overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d2fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d2fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d2numDims = 2
|
|
d2maxNodes = 8
|
|
d2minNodes = d2maxNodes / 2
|
|
d2useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d2unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d2numDims]
|
|
|
|
type d2RTree struct {
|
|
root *d2nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d2rectT struct {
|
|
min [d2numDims]float64 ///< Min dimensions of bounding box
|
|
max [d2numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d2branchT struct {
|
|
rect d2rectT ///< Bounds
|
|
child *d2nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d2nodeT for each branch level
|
|
type d2nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d2maxNodes]d2branchT ///< Branch
|
|
}
|
|
|
|
func (node *d2nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d2nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d2listNodeT struct {
|
|
next *d2listNodeT ///< Next in list
|
|
node *d2nodeT ///< Node
|
|
}
|
|
|
|
const d2notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d2partitionVarsT struct {
|
|
partition [d2maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d2rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d2maxNodes + 1]d2branchT
|
|
branchCount int
|
|
coverSplit d2rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d2New() *d2RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d2RTree{
|
|
root: &d2nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d2RTree) Insert(min, max [d2numDims]float64, dataId interface{}) {
|
|
var branch d2branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d2numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d2insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d2RTree) Remove(min, max [d2numDims]float64, dataId interface{}) {
|
|
var rect d2rectT
|
|
for axis := 0; axis < d2numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d2removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d2search rectangle
|
|
/// \param a_min Min of d2search bounding rect
|
|
/// \param a_max Max of d2search bounding rect
|
|
/// \param a_searchResult d2search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d2RTree) Search(min, max [d2numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d2rectT
|
|
for axis := 0; axis < d2numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d2search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d2RTree) Count() int {
|
|
var count int
|
|
d2countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d2RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d2nodeT{}
|
|
}
|
|
|
|
func d2countRec(node *d2nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d2countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d2insertRectRec(branch *d2branchT, node *d2nodeT, newNode **d2nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d2nodeT
|
|
//var newBranch d2branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d2pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d2insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d2combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d2nodeCover(node.branch[index].child)
|
|
var newBranch d2branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d2nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d2addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d2addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d2insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d2insertRect(branch *d2branchT, root **d2nodeT, level int) bool {
|
|
var newNode *d2nodeT
|
|
|
|
if d2insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d2nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d2branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d2nodeCover(*root)
|
|
newBranch.child = *root
|
|
d2addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d2nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d2addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d2nodeCover(node *d2nodeT) d2rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d2combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d2addBranch(branch *d2branchT, node *d2nodeT, newNode **d2nodeT) bool {
|
|
if node.count < d2maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d2splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d2disconnectBranch(node *d2nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d2pickBranch(rect *d2rectT, node *d2nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d2rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d2calcRectVolume(curRect)
|
|
tempRect = d2combineRect(rect, curRect)
|
|
increase = d2calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d2combineRect(rectA, rectB *d2rectT) d2rectT {
|
|
var newRect d2rectT
|
|
|
|
for index := 0; index < d2numDims; index++ {
|
|
newRect.min[index] = d2fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d2fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d2splitNode(node *d2nodeT, branch *d2branchT, newNode **d2nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d2partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d2getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d2choosePartition(parVars, d2minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d2nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d2loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d2rectVolume(rect *d2rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d2numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d2rectT
|
|
func d2rectSphericalVolume(rect *d2rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d2numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d2numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d2unitSphereVolume)
|
|
} else if d2numDims == 4 {
|
|
return (radius * radius * radius * radius * d2unitSphereVolume)
|
|
} else if d2numDims == 3 {
|
|
return (radius * radius * radius * d2unitSphereVolume)
|
|
} else if d2numDims == 2 {
|
|
return (radius * radius * d2unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d2numDims) * d2unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d2calcRectVolume(rect *d2rectT) float64 {
|
|
if d2useSphericalVolume {
|
|
return d2rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d2rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d2getBranches(node *d2nodeT, branch *d2branchT, parVars *d2partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d2maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d2maxNodes] = *branch
|
|
parVars.branchCount = d2maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d2maxNodes+1; index++ {
|
|
parVars.coverSplit = d2combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d2calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d2choosePartition(parVars *d2partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d2initParVars(parVars, parVars.branchCount, minFill)
|
|
d2pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d2notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d2combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d2combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d2calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d2calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d2classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d2notTaken == parVars.partition[index] {
|
|
d2classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d2loadNodes(nodeA, nodeB *d2nodeT, parVars *d2partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d2nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d2addBranch here will not cause a node split.
|
|
d2addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d2partitionVarsT structure.
|
|
func d2initParVars(parVars *d2partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d2notTaken
|
|
}
|
|
}
|
|
|
|
func d2pickSeeds(parVars *d2partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d2maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d2calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d2combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d2calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d2classify(seed0, 0, parVars)
|
|
d2classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d2classify(index, group int, parVars *d2partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d2combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d2calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d2rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d2removeRect provides for eliminating the root.
|
|
func d2removeRect(rect *d2rectT, id interface{}, root **d2nodeT) bool {
|
|
var reInsertList *d2listNodeT
|
|
|
|
if !d2removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d2insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d2removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d2removeRectRec(rect *d2rectT, id interface{}, node *d2nodeT, listNode **d2listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d2overlap(*rect, node.branch[index].rect) {
|
|
if !d2removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d2minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d2nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d2reInsert(node.branch[index].child, listNode)
|
|
d2disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d2disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d2overlap.
|
|
func d2overlap(rectA, rectB d2rectT) bool {
|
|
for index := 0; index < d2numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d2reInsert(node *d2nodeT, listNode **d2listNodeT) {
|
|
newListNode := &d2listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d2search in an index tree or subtree for all data retangles that d2overlap the argument rectangle.
|
|
func d2search(node *d2nodeT, rect d2rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d2overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d2search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d2overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d3fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d3fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d3numDims = 3
|
|
d3maxNodes = 8
|
|
d3minNodes = d3maxNodes / 2
|
|
d3useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d3unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d3numDims]
|
|
|
|
type d3RTree struct {
|
|
root *d3nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d3rectT struct {
|
|
min [d3numDims]float64 ///< Min dimensions of bounding box
|
|
max [d3numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d3branchT struct {
|
|
rect d3rectT ///< Bounds
|
|
child *d3nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d3nodeT for each branch level
|
|
type d3nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d3maxNodes]d3branchT ///< Branch
|
|
}
|
|
|
|
func (node *d3nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d3nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d3listNodeT struct {
|
|
next *d3listNodeT ///< Next in list
|
|
node *d3nodeT ///< Node
|
|
}
|
|
|
|
const d3notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d3partitionVarsT struct {
|
|
partition [d3maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d3rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d3maxNodes + 1]d3branchT
|
|
branchCount int
|
|
coverSplit d3rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d3New() *d3RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d3RTree{
|
|
root: &d3nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d3RTree) Insert(min, max [d3numDims]float64, dataId interface{}) {
|
|
var branch d3branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d3numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d3insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d3RTree) Remove(min, max [d3numDims]float64, dataId interface{}) {
|
|
var rect d3rectT
|
|
for axis := 0; axis < d3numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d3removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d3search rectangle
|
|
/// \param a_min Min of d3search bounding rect
|
|
/// \param a_max Max of d3search bounding rect
|
|
/// \param a_searchResult d3search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d3RTree) Search(min, max [d3numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d3rectT
|
|
for axis := 0; axis < d3numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d3search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d3RTree) Count() int {
|
|
var count int
|
|
d3countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d3RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d3nodeT{}
|
|
}
|
|
|
|
func d3countRec(node *d3nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d3countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d3insertRectRec(branch *d3branchT, node *d3nodeT, newNode **d3nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d3nodeT
|
|
//var newBranch d3branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d3pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d3insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d3combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d3nodeCover(node.branch[index].child)
|
|
var newBranch d3branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d3nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d3addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d3addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d3insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d3insertRect(branch *d3branchT, root **d3nodeT, level int) bool {
|
|
var newNode *d3nodeT
|
|
|
|
if d3insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d3nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d3branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d3nodeCover(*root)
|
|
newBranch.child = *root
|
|
d3addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d3nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d3addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d3nodeCover(node *d3nodeT) d3rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d3combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d3addBranch(branch *d3branchT, node *d3nodeT, newNode **d3nodeT) bool {
|
|
if node.count < d3maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d3splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d3disconnectBranch(node *d3nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d3pickBranch(rect *d3rectT, node *d3nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d3rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d3calcRectVolume(curRect)
|
|
tempRect = d3combineRect(rect, curRect)
|
|
increase = d3calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d3combineRect(rectA, rectB *d3rectT) d3rectT {
|
|
var newRect d3rectT
|
|
|
|
for index := 0; index < d3numDims; index++ {
|
|
newRect.min[index] = d3fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d3fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d3splitNode(node *d3nodeT, branch *d3branchT, newNode **d3nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d3partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d3getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d3choosePartition(parVars, d3minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d3nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d3loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d3rectVolume(rect *d3rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d3numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d3rectT
|
|
func d3rectSphericalVolume(rect *d3rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d3numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d3numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d3unitSphereVolume)
|
|
} else if d3numDims == 4 {
|
|
return (radius * radius * radius * radius * d3unitSphereVolume)
|
|
} else if d3numDims == 3 {
|
|
return (radius * radius * radius * d3unitSphereVolume)
|
|
} else if d3numDims == 2 {
|
|
return (radius * radius * d3unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d3numDims) * d3unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d3calcRectVolume(rect *d3rectT) float64 {
|
|
if d3useSphericalVolume {
|
|
return d3rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d3rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d3getBranches(node *d3nodeT, branch *d3branchT, parVars *d3partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d3maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d3maxNodes] = *branch
|
|
parVars.branchCount = d3maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d3maxNodes+1; index++ {
|
|
parVars.coverSplit = d3combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d3calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d3choosePartition(parVars *d3partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d3initParVars(parVars, parVars.branchCount, minFill)
|
|
d3pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d3notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d3combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d3combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d3calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d3calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d3classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d3notTaken == parVars.partition[index] {
|
|
d3classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d3loadNodes(nodeA, nodeB *d3nodeT, parVars *d3partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d3nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d3addBranch here will not cause a node split.
|
|
d3addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d3partitionVarsT structure.
|
|
func d3initParVars(parVars *d3partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d3notTaken
|
|
}
|
|
}
|
|
|
|
func d3pickSeeds(parVars *d3partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d3maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d3calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d3combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d3calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d3classify(seed0, 0, parVars)
|
|
d3classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d3classify(index, group int, parVars *d3partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d3combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d3calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d3rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d3removeRect provides for eliminating the root.
|
|
func d3removeRect(rect *d3rectT, id interface{}, root **d3nodeT) bool {
|
|
var reInsertList *d3listNodeT
|
|
|
|
if !d3removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d3insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d3removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d3removeRectRec(rect *d3rectT, id interface{}, node *d3nodeT, listNode **d3listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d3overlap(*rect, node.branch[index].rect) {
|
|
if !d3removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d3minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d3nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d3reInsert(node.branch[index].child, listNode)
|
|
d3disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d3disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d3overlap.
|
|
func d3overlap(rectA, rectB d3rectT) bool {
|
|
for index := 0; index < d3numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d3reInsert(node *d3nodeT, listNode **d3listNodeT) {
|
|
newListNode := &d3listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d3search in an index tree or subtree for all data retangles that d3overlap the argument rectangle.
|
|
func d3search(node *d3nodeT, rect d3rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d3overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d3search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d3overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d4fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d4fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d4numDims = 4
|
|
d4maxNodes = 8
|
|
d4minNodes = d4maxNodes / 2
|
|
d4useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d4unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d4numDims]
|
|
|
|
type d4RTree struct {
|
|
root *d4nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d4rectT struct {
|
|
min [d4numDims]float64 ///< Min dimensions of bounding box
|
|
max [d4numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d4branchT struct {
|
|
rect d4rectT ///< Bounds
|
|
child *d4nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d4nodeT for each branch level
|
|
type d4nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d4maxNodes]d4branchT ///< Branch
|
|
}
|
|
|
|
func (node *d4nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d4nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d4listNodeT struct {
|
|
next *d4listNodeT ///< Next in list
|
|
node *d4nodeT ///< Node
|
|
}
|
|
|
|
const d4notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d4partitionVarsT struct {
|
|
partition [d4maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d4rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d4maxNodes + 1]d4branchT
|
|
branchCount int
|
|
coverSplit d4rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d4New() *d4RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d4RTree{
|
|
root: &d4nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d4RTree) Insert(min, max [d4numDims]float64, dataId interface{}) {
|
|
var branch d4branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d4numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d4insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d4RTree) Remove(min, max [d4numDims]float64, dataId interface{}) {
|
|
var rect d4rectT
|
|
for axis := 0; axis < d4numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d4removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d4search rectangle
|
|
/// \param a_min Min of d4search bounding rect
|
|
/// \param a_max Max of d4search bounding rect
|
|
/// \param a_searchResult d4search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d4RTree) Search(min, max [d4numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d4rectT
|
|
for axis := 0; axis < d4numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d4search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d4RTree) Count() int {
|
|
var count int
|
|
d4countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d4RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d4nodeT{}
|
|
}
|
|
|
|
func d4countRec(node *d4nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d4countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d4insertRectRec(branch *d4branchT, node *d4nodeT, newNode **d4nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d4nodeT
|
|
//var newBranch d4branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d4pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d4insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d4combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d4nodeCover(node.branch[index].child)
|
|
var newBranch d4branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d4nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d4addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d4addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d4insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d4insertRect(branch *d4branchT, root **d4nodeT, level int) bool {
|
|
var newNode *d4nodeT
|
|
|
|
if d4insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d4nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d4branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d4nodeCover(*root)
|
|
newBranch.child = *root
|
|
d4addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d4nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d4addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d4nodeCover(node *d4nodeT) d4rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d4combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d4addBranch(branch *d4branchT, node *d4nodeT, newNode **d4nodeT) bool {
|
|
if node.count < d4maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d4splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d4disconnectBranch(node *d4nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d4pickBranch(rect *d4rectT, node *d4nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d4rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d4calcRectVolume(curRect)
|
|
tempRect = d4combineRect(rect, curRect)
|
|
increase = d4calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d4combineRect(rectA, rectB *d4rectT) d4rectT {
|
|
var newRect d4rectT
|
|
|
|
for index := 0; index < d4numDims; index++ {
|
|
newRect.min[index] = d4fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d4fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d4splitNode(node *d4nodeT, branch *d4branchT, newNode **d4nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d4partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d4getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d4choosePartition(parVars, d4minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d4nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d4loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d4rectVolume(rect *d4rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d4numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d4rectT
|
|
func d4rectSphericalVolume(rect *d4rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d4numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d4numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d4unitSphereVolume)
|
|
} else if d4numDims == 4 {
|
|
return (radius * radius * radius * radius * d4unitSphereVolume)
|
|
} else if d4numDims == 3 {
|
|
return (radius * radius * radius * d4unitSphereVolume)
|
|
} else if d4numDims == 2 {
|
|
return (radius * radius * d4unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d4numDims) * d4unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d4calcRectVolume(rect *d4rectT) float64 {
|
|
if d4useSphericalVolume {
|
|
return d4rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d4rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d4getBranches(node *d4nodeT, branch *d4branchT, parVars *d4partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d4maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d4maxNodes] = *branch
|
|
parVars.branchCount = d4maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d4maxNodes+1; index++ {
|
|
parVars.coverSplit = d4combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d4calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d4choosePartition(parVars *d4partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d4initParVars(parVars, parVars.branchCount, minFill)
|
|
d4pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d4notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d4combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d4combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d4calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d4calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d4classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d4notTaken == parVars.partition[index] {
|
|
d4classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d4loadNodes(nodeA, nodeB *d4nodeT, parVars *d4partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d4nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d4addBranch here will not cause a node split.
|
|
d4addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d4partitionVarsT structure.
|
|
func d4initParVars(parVars *d4partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d4notTaken
|
|
}
|
|
}
|
|
|
|
func d4pickSeeds(parVars *d4partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d4maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d4calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d4combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d4calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d4classify(seed0, 0, parVars)
|
|
d4classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d4classify(index, group int, parVars *d4partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d4combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d4calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d4rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d4removeRect provides for eliminating the root.
|
|
func d4removeRect(rect *d4rectT, id interface{}, root **d4nodeT) bool {
|
|
var reInsertList *d4listNodeT
|
|
|
|
if !d4removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d4insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d4removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d4removeRectRec(rect *d4rectT, id interface{}, node *d4nodeT, listNode **d4listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d4overlap(*rect, node.branch[index].rect) {
|
|
if !d4removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d4minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d4nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d4reInsert(node.branch[index].child, listNode)
|
|
d4disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d4disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d4overlap.
|
|
func d4overlap(rectA, rectB d4rectT) bool {
|
|
for index := 0; index < d4numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d4reInsert(node *d4nodeT, listNode **d4listNodeT) {
|
|
newListNode := &d4listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d4search in an index tree or subtree for all data retangles that d4overlap the argument rectangle.
|
|
func d4search(node *d4nodeT, rect d4rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d4overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d4search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d4overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d5fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d5fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d5numDims = 5
|
|
d5maxNodes = 8
|
|
d5minNodes = d5maxNodes / 2
|
|
d5useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d5unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d5numDims]
|
|
|
|
type d5RTree struct {
|
|
root *d5nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d5rectT struct {
|
|
min [d5numDims]float64 ///< Min dimensions of bounding box
|
|
max [d5numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d5branchT struct {
|
|
rect d5rectT ///< Bounds
|
|
child *d5nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d5nodeT for each branch level
|
|
type d5nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d5maxNodes]d5branchT ///< Branch
|
|
}
|
|
|
|
func (node *d5nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d5nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d5listNodeT struct {
|
|
next *d5listNodeT ///< Next in list
|
|
node *d5nodeT ///< Node
|
|
}
|
|
|
|
const d5notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d5partitionVarsT struct {
|
|
partition [d5maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d5rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d5maxNodes + 1]d5branchT
|
|
branchCount int
|
|
coverSplit d5rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d5New() *d5RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d5RTree{
|
|
root: &d5nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d5RTree) Insert(min, max [d5numDims]float64, dataId interface{}) {
|
|
var branch d5branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d5numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d5insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d5RTree) Remove(min, max [d5numDims]float64, dataId interface{}) {
|
|
var rect d5rectT
|
|
for axis := 0; axis < d5numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d5removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d5search rectangle
|
|
/// \param a_min Min of d5search bounding rect
|
|
/// \param a_max Max of d5search bounding rect
|
|
/// \param a_searchResult d5search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d5RTree) Search(min, max [d5numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d5rectT
|
|
for axis := 0; axis < d5numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d5search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d5RTree) Count() int {
|
|
var count int
|
|
d5countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d5RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d5nodeT{}
|
|
}
|
|
|
|
func d5countRec(node *d5nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d5countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d5insertRectRec(branch *d5branchT, node *d5nodeT, newNode **d5nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d5nodeT
|
|
//var newBranch d5branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d5pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d5insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d5combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d5nodeCover(node.branch[index].child)
|
|
var newBranch d5branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d5nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d5addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d5addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d5insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d5insertRect(branch *d5branchT, root **d5nodeT, level int) bool {
|
|
var newNode *d5nodeT
|
|
|
|
if d5insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d5nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d5branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d5nodeCover(*root)
|
|
newBranch.child = *root
|
|
d5addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d5nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d5addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d5nodeCover(node *d5nodeT) d5rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d5combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d5addBranch(branch *d5branchT, node *d5nodeT, newNode **d5nodeT) bool {
|
|
if node.count < d5maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d5splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d5disconnectBranch(node *d5nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d5pickBranch(rect *d5rectT, node *d5nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d5rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d5calcRectVolume(curRect)
|
|
tempRect = d5combineRect(rect, curRect)
|
|
increase = d5calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d5combineRect(rectA, rectB *d5rectT) d5rectT {
|
|
var newRect d5rectT
|
|
|
|
for index := 0; index < d5numDims; index++ {
|
|
newRect.min[index] = d5fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d5fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d5splitNode(node *d5nodeT, branch *d5branchT, newNode **d5nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d5partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d5getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d5choosePartition(parVars, d5minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d5nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d5loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d5rectVolume(rect *d5rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d5numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d5rectT
|
|
func d5rectSphericalVolume(rect *d5rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d5numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d5numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d5unitSphereVolume)
|
|
} else if d5numDims == 4 {
|
|
return (radius * radius * radius * radius * d5unitSphereVolume)
|
|
} else if d5numDims == 3 {
|
|
return (radius * radius * radius * d5unitSphereVolume)
|
|
} else if d5numDims == 2 {
|
|
return (radius * radius * d5unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d5numDims) * d5unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d5calcRectVolume(rect *d5rectT) float64 {
|
|
if d5useSphericalVolume {
|
|
return d5rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d5rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d5getBranches(node *d5nodeT, branch *d5branchT, parVars *d5partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d5maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d5maxNodes] = *branch
|
|
parVars.branchCount = d5maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d5maxNodes+1; index++ {
|
|
parVars.coverSplit = d5combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d5calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d5choosePartition(parVars *d5partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d5initParVars(parVars, parVars.branchCount, minFill)
|
|
d5pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d5notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d5combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d5combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d5calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d5calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d5classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d5notTaken == parVars.partition[index] {
|
|
d5classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d5loadNodes(nodeA, nodeB *d5nodeT, parVars *d5partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d5nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d5addBranch here will not cause a node split.
|
|
d5addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d5partitionVarsT structure.
|
|
func d5initParVars(parVars *d5partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d5notTaken
|
|
}
|
|
}
|
|
|
|
func d5pickSeeds(parVars *d5partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d5maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d5calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d5combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d5calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d5classify(seed0, 0, parVars)
|
|
d5classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d5classify(index, group int, parVars *d5partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d5combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d5calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d5rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d5removeRect provides for eliminating the root.
|
|
func d5removeRect(rect *d5rectT, id interface{}, root **d5nodeT) bool {
|
|
var reInsertList *d5listNodeT
|
|
|
|
if !d5removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d5insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d5removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d5removeRectRec(rect *d5rectT, id interface{}, node *d5nodeT, listNode **d5listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d5overlap(*rect, node.branch[index].rect) {
|
|
if !d5removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d5minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d5nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d5reInsert(node.branch[index].child, listNode)
|
|
d5disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d5disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d5overlap.
|
|
func d5overlap(rectA, rectB d5rectT) bool {
|
|
for index := 0; index < d5numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d5reInsert(node *d5nodeT, listNode **d5listNodeT) {
|
|
newListNode := &d5listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d5search in an index tree or subtree for all data retangles that d5overlap the argument rectangle.
|
|
func d5search(node *d5nodeT, rect d5rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d5overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d5search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d5overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d6fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d6fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d6numDims = 6
|
|
d6maxNodes = 8
|
|
d6minNodes = d6maxNodes / 2
|
|
d6useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d6unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d6numDims]
|
|
|
|
type d6RTree struct {
|
|
root *d6nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d6rectT struct {
|
|
min [d6numDims]float64 ///< Min dimensions of bounding box
|
|
max [d6numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d6branchT struct {
|
|
rect d6rectT ///< Bounds
|
|
child *d6nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d6nodeT for each branch level
|
|
type d6nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d6maxNodes]d6branchT ///< Branch
|
|
}
|
|
|
|
func (node *d6nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d6nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d6listNodeT struct {
|
|
next *d6listNodeT ///< Next in list
|
|
node *d6nodeT ///< Node
|
|
}
|
|
|
|
const d6notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d6partitionVarsT struct {
|
|
partition [d6maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d6rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d6maxNodes + 1]d6branchT
|
|
branchCount int
|
|
coverSplit d6rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d6New() *d6RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d6RTree{
|
|
root: &d6nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d6RTree) Insert(min, max [d6numDims]float64, dataId interface{}) {
|
|
var branch d6branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d6numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d6insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d6RTree) Remove(min, max [d6numDims]float64, dataId interface{}) {
|
|
var rect d6rectT
|
|
for axis := 0; axis < d6numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d6removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d6search rectangle
|
|
/// \param a_min Min of d6search bounding rect
|
|
/// \param a_max Max of d6search bounding rect
|
|
/// \param a_searchResult d6search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d6RTree) Search(min, max [d6numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d6rectT
|
|
for axis := 0; axis < d6numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d6search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d6RTree) Count() int {
|
|
var count int
|
|
d6countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d6RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d6nodeT{}
|
|
}
|
|
|
|
func d6countRec(node *d6nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d6countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d6insertRectRec(branch *d6branchT, node *d6nodeT, newNode **d6nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d6nodeT
|
|
//var newBranch d6branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d6pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d6insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d6combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d6nodeCover(node.branch[index].child)
|
|
var newBranch d6branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d6nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d6addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d6addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d6insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d6insertRect(branch *d6branchT, root **d6nodeT, level int) bool {
|
|
var newNode *d6nodeT
|
|
|
|
if d6insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d6nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d6branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d6nodeCover(*root)
|
|
newBranch.child = *root
|
|
d6addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d6nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d6addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d6nodeCover(node *d6nodeT) d6rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d6combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d6addBranch(branch *d6branchT, node *d6nodeT, newNode **d6nodeT) bool {
|
|
if node.count < d6maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d6splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d6disconnectBranch(node *d6nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d6pickBranch(rect *d6rectT, node *d6nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d6rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d6calcRectVolume(curRect)
|
|
tempRect = d6combineRect(rect, curRect)
|
|
increase = d6calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d6combineRect(rectA, rectB *d6rectT) d6rectT {
|
|
var newRect d6rectT
|
|
|
|
for index := 0; index < d6numDims; index++ {
|
|
newRect.min[index] = d6fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d6fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d6splitNode(node *d6nodeT, branch *d6branchT, newNode **d6nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d6partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d6getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d6choosePartition(parVars, d6minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d6nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d6loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d6rectVolume(rect *d6rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d6numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d6rectT
|
|
func d6rectSphericalVolume(rect *d6rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d6numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d6numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d6unitSphereVolume)
|
|
} else if d6numDims == 4 {
|
|
return (radius * radius * radius * radius * d6unitSphereVolume)
|
|
} else if d6numDims == 3 {
|
|
return (radius * radius * radius * d6unitSphereVolume)
|
|
} else if d6numDims == 2 {
|
|
return (radius * radius * d6unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d6numDims) * d6unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d6calcRectVolume(rect *d6rectT) float64 {
|
|
if d6useSphericalVolume {
|
|
return d6rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d6rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d6getBranches(node *d6nodeT, branch *d6branchT, parVars *d6partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d6maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d6maxNodes] = *branch
|
|
parVars.branchCount = d6maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d6maxNodes+1; index++ {
|
|
parVars.coverSplit = d6combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d6calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d6choosePartition(parVars *d6partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d6initParVars(parVars, parVars.branchCount, minFill)
|
|
d6pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d6notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d6combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d6combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d6calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d6calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d6classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d6notTaken == parVars.partition[index] {
|
|
d6classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d6loadNodes(nodeA, nodeB *d6nodeT, parVars *d6partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d6nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d6addBranch here will not cause a node split.
|
|
d6addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d6partitionVarsT structure.
|
|
func d6initParVars(parVars *d6partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d6notTaken
|
|
}
|
|
}
|
|
|
|
func d6pickSeeds(parVars *d6partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d6maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d6calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d6combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d6calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d6classify(seed0, 0, parVars)
|
|
d6classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d6classify(index, group int, parVars *d6partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d6combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d6calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d6rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d6removeRect provides for eliminating the root.
|
|
func d6removeRect(rect *d6rectT, id interface{}, root **d6nodeT) bool {
|
|
var reInsertList *d6listNodeT
|
|
|
|
if !d6removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d6insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d6removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d6removeRectRec(rect *d6rectT, id interface{}, node *d6nodeT, listNode **d6listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d6overlap(*rect, node.branch[index].rect) {
|
|
if !d6removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d6minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d6nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d6reInsert(node.branch[index].child, listNode)
|
|
d6disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d6disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d6overlap.
|
|
func d6overlap(rectA, rectB d6rectT) bool {
|
|
for index := 0; index < d6numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d6reInsert(node *d6nodeT, listNode **d6listNodeT) {
|
|
newListNode := &d6listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d6search in an index tree or subtree for all data retangles that d6overlap the argument rectangle.
|
|
func d6search(node *d6nodeT, rect d6rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d6overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d6search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d6overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d7fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d7fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d7numDims = 7
|
|
d7maxNodes = 8
|
|
d7minNodes = d7maxNodes / 2
|
|
d7useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d7unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d7numDims]
|
|
|
|
type d7RTree struct {
|
|
root *d7nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d7rectT struct {
|
|
min [d7numDims]float64 ///< Min dimensions of bounding box
|
|
max [d7numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d7branchT struct {
|
|
rect d7rectT ///< Bounds
|
|
child *d7nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d7nodeT for each branch level
|
|
type d7nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d7maxNodes]d7branchT ///< Branch
|
|
}
|
|
|
|
func (node *d7nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d7nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d7listNodeT struct {
|
|
next *d7listNodeT ///< Next in list
|
|
node *d7nodeT ///< Node
|
|
}
|
|
|
|
const d7notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d7partitionVarsT struct {
|
|
partition [d7maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d7rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d7maxNodes + 1]d7branchT
|
|
branchCount int
|
|
coverSplit d7rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d7New() *d7RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d7RTree{
|
|
root: &d7nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d7RTree) Insert(min, max [d7numDims]float64, dataId interface{}) {
|
|
var branch d7branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d7numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d7insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d7RTree) Remove(min, max [d7numDims]float64, dataId interface{}) {
|
|
var rect d7rectT
|
|
for axis := 0; axis < d7numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d7removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d7search rectangle
|
|
/// \param a_min Min of d7search bounding rect
|
|
/// \param a_max Max of d7search bounding rect
|
|
/// \param a_searchResult d7search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d7RTree) Search(min, max [d7numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d7rectT
|
|
for axis := 0; axis < d7numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d7search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d7RTree) Count() int {
|
|
var count int
|
|
d7countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d7RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d7nodeT{}
|
|
}
|
|
|
|
func d7countRec(node *d7nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d7countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d7insertRectRec(branch *d7branchT, node *d7nodeT, newNode **d7nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d7nodeT
|
|
//var newBranch d7branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d7pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d7insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d7combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d7nodeCover(node.branch[index].child)
|
|
var newBranch d7branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d7nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d7addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d7addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d7insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d7insertRect(branch *d7branchT, root **d7nodeT, level int) bool {
|
|
var newNode *d7nodeT
|
|
|
|
if d7insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d7nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d7branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d7nodeCover(*root)
|
|
newBranch.child = *root
|
|
d7addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d7nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d7addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d7nodeCover(node *d7nodeT) d7rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d7combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d7addBranch(branch *d7branchT, node *d7nodeT, newNode **d7nodeT) bool {
|
|
if node.count < d7maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d7splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d7disconnectBranch(node *d7nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d7pickBranch(rect *d7rectT, node *d7nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d7rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d7calcRectVolume(curRect)
|
|
tempRect = d7combineRect(rect, curRect)
|
|
increase = d7calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d7combineRect(rectA, rectB *d7rectT) d7rectT {
|
|
var newRect d7rectT
|
|
|
|
for index := 0; index < d7numDims; index++ {
|
|
newRect.min[index] = d7fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d7fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d7splitNode(node *d7nodeT, branch *d7branchT, newNode **d7nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d7partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d7getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d7choosePartition(parVars, d7minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d7nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d7loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d7rectVolume(rect *d7rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d7numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d7rectT
|
|
func d7rectSphericalVolume(rect *d7rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d7numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d7numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d7unitSphereVolume)
|
|
} else if d7numDims == 4 {
|
|
return (radius * radius * radius * radius * d7unitSphereVolume)
|
|
} else if d7numDims == 3 {
|
|
return (radius * radius * radius * d7unitSphereVolume)
|
|
} else if d7numDims == 2 {
|
|
return (radius * radius * d7unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d7numDims) * d7unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d7calcRectVolume(rect *d7rectT) float64 {
|
|
if d7useSphericalVolume {
|
|
return d7rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d7rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d7getBranches(node *d7nodeT, branch *d7branchT, parVars *d7partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d7maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d7maxNodes] = *branch
|
|
parVars.branchCount = d7maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d7maxNodes+1; index++ {
|
|
parVars.coverSplit = d7combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d7calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d7choosePartition(parVars *d7partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d7initParVars(parVars, parVars.branchCount, minFill)
|
|
d7pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d7notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d7combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d7combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d7calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d7calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d7classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d7notTaken == parVars.partition[index] {
|
|
d7classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d7loadNodes(nodeA, nodeB *d7nodeT, parVars *d7partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d7nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d7addBranch here will not cause a node split.
|
|
d7addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d7partitionVarsT structure.
|
|
func d7initParVars(parVars *d7partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d7notTaken
|
|
}
|
|
}
|
|
|
|
func d7pickSeeds(parVars *d7partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d7maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d7calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d7combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d7calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d7classify(seed0, 0, parVars)
|
|
d7classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d7classify(index, group int, parVars *d7partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d7combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d7calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d7rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d7removeRect provides for eliminating the root.
|
|
func d7removeRect(rect *d7rectT, id interface{}, root **d7nodeT) bool {
|
|
var reInsertList *d7listNodeT
|
|
|
|
if !d7removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d7insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d7removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d7removeRectRec(rect *d7rectT, id interface{}, node *d7nodeT, listNode **d7listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d7overlap(*rect, node.branch[index].rect) {
|
|
if !d7removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d7minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d7nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d7reInsert(node.branch[index].child, listNode)
|
|
d7disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d7disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d7overlap.
|
|
func d7overlap(rectA, rectB d7rectT) bool {
|
|
for index := 0; index < d7numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d7reInsert(node *d7nodeT, listNode **d7listNodeT) {
|
|
newListNode := &d7listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d7search in an index tree or subtree for all data retangles that d7overlap the argument rectangle.
|
|
func d7search(node *d7nodeT, rect d7rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d7overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d7search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d7overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d8fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d8fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d8numDims = 8
|
|
d8maxNodes = 8
|
|
d8minNodes = d8maxNodes / 2
|
|
d8useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d8unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d8numDims]
|
|
|
|
type d8RTree struct {
|
|
root *d8nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d8rectT struct {
|
|
min [d8numDims]float64 ///< Min dimensions of bounding box
|
|
max [d8numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d8branchT struct {
|
|
rect d8rectT ///< Bounds
|
|
child *d8nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d8nodeT for each branch level
|
|
type d8nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d8maxNodes]d8branchT ///< Branch
|
|
}
|
|
|
|
func (node *d8nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d8nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d8listNodeT struct {
|
|
next *d8listNodeT ///< Next in list
|
|
node *d8nodeT ///< Node
|
|
}
|
|
|
|
const d8notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d8partitionVarsT struct {
|
|
partition [d8maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d8rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d8maxNodes + 1]d8branchT
|
|
branchCount int
|
|
coverSplit d8rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d8New() *d8RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d8RTree{
|
|
root: &d8nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d8RTree) Insert(min, max [d8numDims]float64, dataId interface{}) {
|
|
var branch d8branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d8numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d8insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d8RTree) Remove(min, max [d8numDims]float64, dataId interface{}) {
|
|
var rect d8rectT
|
|
for axis := 0; axis < d8numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d8removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d8search rectangle
|
|
/// \param a_min Min of d8search bounding rect
|
|
/// \param a_max Max of d8search bounding rect
|
|
/// \param a_searchResult d8search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d8RTree) Search(min, max [d8numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d8rectT
|
|
for axis := 0; axis < d8numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d8search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d8RTree) Count() int {
|
|
var count int
|
|
d8countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d8RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d8nodeT{}
|
|
}
|
|
|
|
func d8countRec(node *d8nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d8countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d8insertRectRec(branch *d8branchT, node *d8nodeT, newNode **d8nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d8nodeT
|
|
//var newBranch d8branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d8pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d8insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d8combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d8nodeCover(node.branch[index].child)
|
|
var newBranch d8branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d8nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d8addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d8addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d8insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d8insertRect(branch *d8branchT, root **d8nodeT, level int) bool {
|
|
var newNode *d8nodeT
|
|
|
|
if d8insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d8nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d8branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d8nodeCover(*root)
|
|
newBranch.child = *root
|
|
d8addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d8nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d8addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d8nodeCover(node *d8nodeT) d8rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d8combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d8addBranch(branch *d8branchT, node *d8nodeT, newNode **d8nodeT) bool {
|
|
if node.count < d8maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d8splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d8disconnectBranch(node *d8nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d8pickBranch(rect *d8rectT, node *d8nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d8rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d8calcRectVolume(curRect)
|
|
tempRect = d8combineRect(rect, curRect)
|
|
increase = d8calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d8combineRect(rectA, rectB *d8rectT) d8rectT {
|
|
var newRect d8rectT
|
|
|
|
for index := 0; index < d8numDims; index++ {
|
|
newRect.min[index] = d8fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d8fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d8splitNode(node *d8nodeT, branch *d8branchT, newNode **d8nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d8partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d8getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d8choosePartition(parVars, d8minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d8nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d8loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d8rectVolume(rect *d8rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d8numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d8rectT
|
|
func d8rectSphericalVolume(rect *d8rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d8numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d8numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d8unitSphereVolume)
|
|
} else if d8numDims == 4 {
|
|
return (radius * radius * radius * radius * d8unitSphereVolume)
|
|
} else if d8numDims == 3 {
|
|
return (radius * radius * radius * d8unitSphereVolume)
|
|
} else if d8numDims == 2 {
|
|
return (radius * radius * d8unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d8numDims) * d8unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d8calcRectVolume(rect *d8rectT) float64 {
|
|
if d8useSphericalVolume {
|
|
return d8rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d8rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d8getBranches(node *d8nodeT, branch *d8branchT, parVars *d8partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d8maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d8maxNodes] = *branch
|
|
parVars.branchCount = d8maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d8maxNodes+1; index++ {
|
|
parVars.coverSplit = d8combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d8calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d8choosePartition(parVars *d8partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d8initParVars(parVars, parVars.branchCount, minFill)
|
|
d8pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d8notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d8combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d8combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d8calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d8calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d8classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d8notTaken == parVars.partition[index] {
|
|
d8classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d8loadNodes(nodeA, nodeB *d8nodeT, parVars *d8partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d8nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d8addBranch here will not cause a node split.
|
|
d8addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d8partitionVarsT structure.
|
|
func d8initParVars(parVars *d8partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d8notTaken
|
|
}
|
|
}
|
|
|
|
func d8pickSeeds(parVars *d8partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d8maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d8calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d8combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d8calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d8classify(seed0, 0, parVars)
|
|
d8classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d8classify(index, group int, parVars *d8partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d8combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d8calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d8rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d8removeRect provides for eliminating the root.
|
|
func d8removeRect(rect *d8rectT, id interface{}, root **d8nodeT) bool {
|
|
var reInsertList *d8listNodeT
|
|
|
|
if !d8removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d8insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d8removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d8removeRectRec(rect *d8rectT, id interface{}, node *d8nodeT, listNode **d8listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d8overlap(*rect, node.branch[index].rect) {
|
|
if !d8removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d8minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d8nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d8reInsert(node.branch[index].child, listNode)
|
|
d8disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d8disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d8overlap.
|
|
func d8overlap(rectA, rectB d8rectT) bool {
|
|
for index := 0; index < d8numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d8reInsert(node *d8nodeT, listNode **d8listNodeT) {
|
|
newListNode := &d8listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d8search in an index tree or subtree for all data retangles that d8overlap the argument rectangle.
|
|
func d8search(node *d8nodeT, rect d8rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d8overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d8search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d8overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d9fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d9fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d9numDims = 9
|
|
d9maxNodes = 8
|
|
d9minNodes = d9maxNodes / 2
|
|
d9useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d9unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d9numDims]
|
|
|
|
type d9RTree struct {
|
|
root *d9nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d9rectT struct {
|
|
min [d9numDims]float64 ///< Min dimensions of bounding box
|
|
max [d9numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d9branchT struct {
|
|
rect d9rectT ///< Bounds
|
|
child *d9nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d9nodeT for each branch level
|
|
type d9nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d9maxNodes]d9branchT ///< Branch
|
|
}
|
|
|
|
func (node *d9nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d9nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d9listNodeT struct {
|
|
next *d9listNodeT ///< Next in list
|
|
node *d9nodeT ///< Node
|
|
}
|
|
|
|
const d9notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d9partitionVarsT struct {
|
|
partition [d9maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d9rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d9maxNodes + 1]d9branchT
|
|
branchCount int
|
|
coverSplit d9rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d9New() *d9RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d9RTree{
|
|
root: &d9nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d9RTree) Insert(min, max [d9numDims]float64, dataId interface{}) {
|
|
var branch d9branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d9numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d9insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d9RTree) Remove(min, max [d9numDims]float64, dataId interface{}) {
|
|
var rect d9rectT
|
|
for axis := 0; axis < d9numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d9removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d9search rectangle
|
|
/// \param a_min Min of d9search bounding rect
|
|
/// \param a_max Max of d9search bounding rect
|
|
/// \param a_searchResult d9search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d9RTree) Search(min, max [d9numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d9rectT
|
|
for axis := 0; axis < d9numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d9search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d9RTree) Count() int {
|
|
var count int
|
|
d9countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d9RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d9nodeT{}
|
|
}
|
|
|
|
func d9countRec(node *d9nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d9countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d9insertRectRec(branch *d9branchT, node *d9nodeT, newNode **d9nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d9nodeT
|
|
//var newBranch d9branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d9pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d9insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d9combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d9nodeCover(node.branch[index].child)
|
|
var newBranch d9branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d9nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d9addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d9addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d9insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d9insertRect(branch *d9branchT, root **d9nodeT, level int) bool {
|
|
var newNode *d9nodeT
|
|
|
|
if d9insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d9nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d9branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d9nodeCover(*root)
|
|
newBranch.child = *root
|
|
d9addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d9nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d9addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d9nodeCover(node *d9nodeT) d9rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d9combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d9addBranch(branch *d9branchT, node *d9nodeT, newNode **d9nodeT) bool {
|
|
if node.count < d9maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d9splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d9disconnectBranch(node *d9nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d9pickBranch(rect *d9rectT, node *d9nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d9rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d9calcRectVolume(curRect)
|
|
tempRect = d9combineRect(rect, curRect)
|
|
increase = d9calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d9combineRect(rectA, rectB *d9rectT) d9rectT {
|
|
var newRect d9rectT
|
|
|
|
for index := 0; index < d9numDims; index++ {
|
|
newRect.min[index] = d9fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d9fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d9splitNode(node *d9nodeT, branch *d9branchT, newNode **d9nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d9partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d9getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d9choosePartition(parVars, d9minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d9nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d9loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d9rectVolume(rect *d9rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d9numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d9rectT
|
|
func d9rectSphericalVolume(rect *d9rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d9numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d9numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d9unitSphereVolume)
|
|
} else if d9numDims == 4 {
|
|
return (radius * radius * radius * radius * d9unitSphereVolume)
|
|
} else if d9numDims == 3 {
|
|
return (radius * radius * radius * d9unitSphereVolume)
|
|
} else if d9numDims == 2 {
|
|
return (radius * radius * d9unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d9numDims) * d9unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d9calcRectVolume(rect *d9rectT) float64 {
|
|
if d9useSphericalVolume {
|
|
return d9rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d9rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d9getBranches(node *d9nodeT, branch *d9branchT, parVars *d9partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d9maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d9maxNodes] = *branch
|
|
parVars.branchCount = d9maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d9maxNodes+1; index++ {
|
|
parVars.coverSplit = d9combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d9calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d9choosePartition(parVars *d9partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d9initParVars(parVars, parVars.branchCount, minFill)
|
|
d9pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d9notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d9combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d9combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d9calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d9calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d9classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d9notTaken == parVars.partition[index] {
|
|
d9classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d9loadNodes(nodeA, nodeB *d9nodeT, parVars *d9partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d9nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d9addBranch here will not cause a node split.
|
|
d9addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d9partitionVarsT structure.
|
|
func d9initParVars(parVars *d9partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d9notTaken
|
|
}
|
|
}
|
|
|
|
func d9pickSeeds(parVars *d9partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d9maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d9calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d9combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d9calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d9classify(seed0, 0, parVars)
|
|
d9classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d9classify(index, group int, parVars *d9partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d9combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d9calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d9rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d9removeRect provides for eliminating the root.
|
|
func d9removeRect(rect *d9rectT, id interface{}, root **d9nodeT) bool {
|
|
var reInsertList *d9listNodeT
|
|
|
|
if !d9removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d9insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d9removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d9removeRectRec(rect *d9rectT, id interface{}, node *d9nodeT, listNode **d9listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d9overlap(*rect, node.branch[index].rect) {
|
|
if !d9removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d9minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d9nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d9reInsert(node.branch[index].child, listNode)
|
|
d9disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d9disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d9overlap.
|
|
func d9overlap(rectA, rectB d9rectT) bool {
|
|
for index := 0; index < d9numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d9reInsert(node *d9nodeT, listNode **d9listNodeT) {
|
|
newListNode := &d9listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d9search in an index tree or subtree for all data retangles that d9overlap the argument rectangle.
|
|
func d9search(node *d9nodeT, rect d9rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d9overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d9search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d9overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d10fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d10fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d10numDims = 10
|
|
d10maxNodes = 8
|
|
d10minNodes = d10maxNodes / 2
|
|
d10useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d10unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d10numDims]
|
|
|
|
type d10RTree struct {
|
|
root *d10nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d10rectT struct {
|
|
min [d10numDims]float64 ///< Min dimensions of bounding box
|
|
max [d10numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d10branchT struct {
|
|
rect d10rectT ///< Bounds
|
|
child *d10nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d10nodeT for each branch level
|
|
type d10nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d10maxNodes]d10branchT ///< Branch
|
|
}
|
|
|
|
func (node *d10nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d10nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d10listNodeT struct {
|
|
next *d10listNodeT ///< Next in list
|
|
node *d10nodeT ///< Node
|
|
}
|
|
|
|
const d10notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d10partitionVarsT struct {
|
|
partition [d10maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d10rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d10maxNodes + 1]d10branchT
|
|
branchCount int
|
|
coverSplit d10rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d10New() *d10RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d10RTree{
|
|
root: &d10nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d10RTree) Insert(min, max [d10numDims]float64, dataId interface{}) {
|
|
var branch d10branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d10numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d10insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d10RTree) Remove(min, max [d10numDims]float64, dataId interface{}) {
|
|
var rect d10rectT
|
|
for axis := 0; axis < d10numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d10removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d10search rectangle
|
|
/// \param a_min Min of d10search bounding rect
|
|
/// \param a_max Max of d10search bounding rect
|
|
/// \param a_searchResult d10search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d10RTree) Search(min, max [d10numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d10rectT
|
|
for axis := 0; axis < d10numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d10search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d10RTree) Count() int {
|
|
var count int
|
|
d10countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d10RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d10nodeT{}
|
|
}
|
|
|
|
func d10countRec(node *d10nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d10countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d10insertRectRec(branch *d10branchT, node *d10nodeT, newNode **d10nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d10nodeT
|
|
//var newBranch d10branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d10pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d10insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d10combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d10nodeCover(node.branch[index].child)
|
|
var newBranch d10branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d10nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d10addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d10addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d10insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d10insertRect(branch *d10branchT, root **d10nodeT, level int) bool {
|
|
var newNode *d10nodeT
|
|
|
|
if d10insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d10nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d10branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d10nodeCover(*root)
|
|
newBranch.child = *root
|
|
d10addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d10nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d10addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d10nodeCover(node *d10nodeT) d10rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d10combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d10addBranch(branch *d10branchT, node *d10nodeT, newNode **d10nodeT) bool {
|
|
if node.count < d10maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d10splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d10disconnectBranch(node *d10nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d10pickBranch(rect *d10rectT, node *d10nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d10rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d10calcRectVolume(curRect)
|
|
tempRect = d10combineRect(rect, curRect)
|
|
increase = d10calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d10combineRect(rectA, rectB *d10rectT) d10rectT {
|
|
var newRect d10rectT
|
|
|
|
for index := 0; index < d10numDims; index++ {
|
|
newRect.min[index] = d10fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d10fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d10splitNode(node *d10nodeT, branch *d10branchT, newNode **d10nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d10partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d10getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d10choosePartition(parVars, d10minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d10nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d10loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d10rectVolume(rect *d10rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d10numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d10rectT
|
|
func d10rectSphericalVolume(rect *d10rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d10numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d10numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d10unitSphereVolume)
|
|
} else if d10numDims == 4 {
|
|
return (radius * radius * radius * radius * d10unitSphereVolume)
|
|
} else if d10numDims == 3 {
|
|
return (radius * radius * radius * d10unitSphereVolume)
|
|
} else if d10numDims == 2 {
|
|
return (radius * radius * d10unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d10numDims) * d10unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d10calcRectVolume(rect *d10rectT) float64 {
|
|
if d10useSphericalVolume {
|
|
return d10rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d10rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d10getBranches(node *d10nodeT, branch *d10branchT, parVars *d10partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d10maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d10maxNodes] = *branch
|
|
parVars.branchCount = d10maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d10maxNodes+1; index++ {
|
|
parVars.coverSplit = d10combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d10calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d10choosePartition(parVars *d10partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d10initParVars(parVars, parVars.branchCount, minFill)
|
|
d10pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d10notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d10combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d10combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d10calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d10calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d10classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d10notTaken == parVars.partition[index] {
|
|
d10classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d10loadNodes(nodeA, nodeB *d10nodeT, parVars *d10partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d10nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d10addBranch here will not cause a node split.
|
|
d10addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d10partitionVarsT structure.
|
|
func d10initParVars(parVars *d10partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d10notTaken
|
|
}
|
|
}
|
|
|
|
func d10pickSeeds(parVars *d10partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d10maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d10calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d10combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d10calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d10classify(seed0, 0, parVars)
|
|
d10classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d10classify(index, group int, parVars *d10partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d10combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d10calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d10rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d10removeRect provides for eliminating the root.
|
|
func d10removeRect(rect *d10rectT, id interface{}, root **d10nodeT) bool {
|
|
var reInsertList *d10listNodeT
|
|
|
|
if !d10removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d10insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d10removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d10removeRectRec(rect *d10rectT, id interface{}, node *d10nodeT, listNode **d10listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d10overlap(*rect, node.branch[index].rect) {
|
|
if !d10removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d10minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d10nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d10reInsert(node.branch[index].child, listNode)
|
|
d10disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d10disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d10overlap.
|
|
func d10overlap(rectA, rectB d10rectT) bool {
|
|
for index := 0; index < d10numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d10reInsert(node *d10nodeT, listNode **d10listNodeT) {
|
|
newListNode := &d10listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d10search in an index tree or subtree for all data retangles that d10overlap the argument rectangle.
|
|
func d10search(node *d10nodeT, rect d10rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d10overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d10search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d10overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d11fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d11fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d11numDims = 11
|
|
d11maxNodes = 8
|
|
d11minNodes = d11maxNodes / 2
|
|
d11useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d11unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d11numDims]
|
|
|
|
type d11RTree struct {
|
|
root *d11nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d11rectT struct {
|
|
min [d11numDims]float64 ///< Min dimensions of bounding box
|
|
max [d11numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d11branchT struct {
|
|
rect d11rectT ///< Bounds
|
|
child *d11nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d11nodeT for each branch level
|
|
type d11nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d11maxNodes]d11branchT ///< Branch
|
|
}
|
|
|
|
func (node *d11nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d11nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d11listNodeT struct {
|
|
next *d11listNodeT ///< Next in list
|
|
node *d11nodeT ///< Node
|
|
}
|
|
|
|
const d11notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d11partitionVarsT struct {
|
|
partition [d11maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d11rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d11maxNodes + 1]d11branchT
|
|
branchCount int
|
|
coverSplit d11rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d11New() *d11RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d11RTree{
|
|
root: &d11nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d11RTree) Insert(min, max [d11numDims]float64, dataId interface{}) {
|
|
var branch d11branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d11numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d11insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d11RTree) Remove(min, max [d11numDims]float64, dataId interface{}) {
|
|
var rect d11rectT
|
|
for axis := 0; axis < d11numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d11removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d11search rectangle
|
|
/// \param a_min Min of d11search bounding rect
|
|
/// \param a_max Max of d11search bounding rect
|
|
/// \param a_searchResult d11search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d11RTree) Search(min, max [d11numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d11rectT
|
|
for axis := 0; axis < d11numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d11search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d11RTree) Count() int {
|
|
var count int
|
|
d11countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d11RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d11nodeT{}
|
|
}
|
|
|
|
func d11countRec(node *d11nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d11countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d11insertRectRec(branch *d11branchT, node *d11nodeT, newNode **d11nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d11nodeT
|
|
//var newBranch d11branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d11pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d11insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d11combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d11nodeCover(node.branch[index].child)
|
|
var newBranch d11branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d11nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d11addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d11addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d11insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d11insertRect(branch *d11branchT, root **d11nodeT, level int) bool {
|
|
var newNode *d11nodeT
|
|
|
|
if d11insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d11nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d11branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d11nodeCover(*root)
|
|
newBranch.child = *root
|
|
d11addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d11nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d11addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d11nodeCover(node *d11nodeT) d11rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d11combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d11addBranch(branch *d11branchT, node *d11nodeT, newNode **d11nodeT) bool {
|
|
if node.count < d11maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d11splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d11disconnectBranch(node *d11nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d11pickBranch(rect *d11rectT, node *d11nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d11rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d11calcRectVolume(curRect)
|
|
tempRect = d11combineRect(rect, curRect)
|
|
increase = d11calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d11combineRect(rectA, rectB *d11rectT) d11rectT {
|
|
var newRect d11rectT
|
|
|
|
for index := 0; index < d11numDims; index++ {
|
|
newRect.min[index] = d11fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d11fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d11splitNode(node *d11nodeT, branch *d11branchT, newNode **d11nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d11partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d11getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d11choosePartition(parVars, d11minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d11nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d11loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d11rectVolume(rect *d11rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d11numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d11rectT
|
|
func d11rectSphericalVolume(rect *d11rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d11numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d11numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d11unitSphereVolume)
|
|
} else if d11numDims == 4 {
|
|
return (radius * radius * radius * radius * d11unitSphereVolume)
|
|
} else if d11numDims == 3 {
|
|
return (radius * radius * radius * d11unitSphereVolume)
|
|
} else if d11numDims == 2 {
|
|
return (radius * radius * d11unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d11numDims) * d11unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d11calcRectVolume(rect *d11rectT) float64 {
|
|
if d11useSphericalVolume {
|
|
return d11rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d11rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d11getBranches(node *d11nodeT, branch *d11branchT, parVars *d11partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d11maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d11maxNodes] = *branch
|
|
parVars.branchCount = d11maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d11maxNodes+1; index++ {
|
|
parVars.coverSplit = d11combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d11calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d11choosePartition(parVars *d11partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d11initParVars(parVars, parVars.branchCount, minFill)
|
|
d11pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d11notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d11combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d11combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d11calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d11calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d11classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d11notTaken == parVars.partition[index] {
|
|
d11classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d11loadNodes(nodeA, nodeB *d11nodeT, parVars *d11partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d11nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d11addBranch here will not cause a node split.
|
|
d11addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d11partitionVarsT structure.
|
|
func d11initParVars(parVars *d11partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d11notTaken
|
|
}
|
|
}
|
|
|
|
func d11pickSeeds(parVars *d11partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d11maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d11calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d11combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d11calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d11classify(seed0, 0, parVars)
|
|
d11classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d11classify(index, group int, parVars *d11partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d11combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d11calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d11rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d11removeRect provides for eliminating the root.
|
|
func d11removeRect(rect *d11rectT, id interface{}, root **d11nodeT) bool {
|
|
var reInsertList *d11listNodeT
|
|
|
|
if !d11removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d11insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d11removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d11removeRectRec(rect *d11rectT, id interface{}, node *d11nodeT, listNode **d11listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d11overlap(*rect, node.branch[index].rect) {
|
|
if !d11removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d11minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d11nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d11reInsert(node.branch[index].child, listNode)
|
|
d11disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d11disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d11overlap.
|
|
func d11overlap(rectA, rectB d11rectT) bool {
|
|
for index := 0; index < d11numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d11reInsert(node *d11nodeT, listNode **d11listNodeT) {
|
|
newListNode := &d11listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d11search in an index tree or subtree for all data retangles that d11overlap the argument rectangle.
|
|
func d11search(node *d11nodeT, rect d11rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d11overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d11search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d11overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d12fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d12fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d12numDims = 12
|
|
d12maxNodes = 8
|
|
d12minNodes = d12maxNodes / 2
|
|
d12useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d12unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d12numDims]
|
|
|
|
type d12RTree struct {
|
|
root *d12nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d12rectT struct {
|
|
min [d12numDims]float64 ///< Min dimensions of bounding box
|
|
max [d12numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d12branchT struct {
|
|
rect d12rectT ///< Bounds
|
|
child *d12nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d12nodeT for each branch level
|
|
type d12nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d12maxNodes]d12branchT ///< Branch
|
|
}
|
|
|
|
func (node *d12nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d12nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d12listNodeT struct {
|
|
next *d12listNodeT ///< Next in list
|
|
node *d12nodeT ///< Node
|
|
}
|
|
|
|
const d12notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d12partitionVarsT struct {
|
|
partition [d12maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d12rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d12maxNodes + 1]d12branchT
|
|
branchCount int
|
|
coverSplit d12rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d12New() *d12RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d12RTree{
|
|
root: &d12nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d12RTree) Insert(min, max [d12numDims]float64, dataId interface{}) {
|
|
var branch d12branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d12numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d12insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d12RTree) Remove(min, max [d12numDims]float64, dataId interface{}) {
|
|
var rect d12rectT
|
|
for axis := 0; axis < d12numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d12removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d12search rectangle
|
|
/// \param a_min Min of d12search bounding rect
|
|
/// \param a_max Max of d12search bounding rect
|
|
/// \param a_searchResult d12search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d12RTree) Search(min, max [d12numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d12rectT
|
|
for axis := 0; axis < d12numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d12search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d12RTree) Count() int {
|
|
var count int
|
|
d12countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d12RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d12nodeT{}
|
|
}
|
|
|
|
func d12countRec(node *d12nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d12countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d12insertRectRec(branch *d12branchT, node *d12nodeT, newNode **d12nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d12nodeT
|
|
//var newBranch d12branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d12pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d12insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d12combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d12nodeCover(node.branch[index].child)
|
|
var newBranch d12branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d12nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d12addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d12addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d12insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d12insertRect(branch *d12branchT, root **d12nodeT, level int) bool {
|
|
var newNode *d12nodeT
|
|
|
|
if d12insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d12nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d12branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d12nodeCover(*root)
|
|
newBranch.child = *root
|
|
d12addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d12nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d12addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d12nodeCover(node *d12nodeT) d12rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d12combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d12addBranch(branch *d12branchT, node *d12nodeT, newNode **d12nodeT) bool {
|
|
if node.count < d12maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d12splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d12disconnectBranch(node *d12nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d12pickBranch(rect *d12rectT, node *d12nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d12rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d12calcRectVolume(curRect)
|
|
tempRect = d12combineRect(rect, curRect)
|
|
increase = d12calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d12combineRect(rectA, rectB *d12rectT) d12rectT {
|
|
var newRect d12rectT
|
|
|
|
for index := 0; index < d12numDims; index++ {
|
|
newRect.min[index] = d12fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d12fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d12splitNode(node *d12nodeT, branch *d12branchT, newNode **d12nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d12partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d12getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d12choosePartition(parVars, d12minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d12nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d12loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d12rectVolume(rect *d12rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d12numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d12rectT
|
|
func d12rectSphericalVolume(rect *d12rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d12numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d12numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d12unitSphereVolume)
|
|
} else if d12numDims == 4 {
|
|
return (radius * radius * radius * radius * d12unitSphereVolume)
|
|
} else if d12numDims == 3 {
|
|
return (radius * radius * radius * d12unitSphereVolume)
|
|
} else if d12numDims == 2 {
|
|
return (radius * radius * d12unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d12numDims) * d12unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d12calcRectVolume(rect *d12rectT) float64 {
|
|
if d12useSphericalVolume {
|
|
return d12rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d12rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d12getBranches(node *d12nodeT, branch *d12branchT, parVars *d12partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d12maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d12maxNodes] = *branch
|
|
parVars.branchCount = d12maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d12maxNodes+1; index++ {
|
|
parVars.coverSplit = d12combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d12calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d12choosePartition(parVars *d12partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d12initParVars(parVars, parVars.branchCount, minFill)
|
|
d12pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d12notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d12combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d12combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d12calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d12calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d12classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d12notTaken == parVars.partition[index] {
|
|
d12classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d12loadNodes(nodeA, nodeB *d12nodeT, parVars *d12partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d12nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d12addBranch here will not cause a node split.
|
|
d12addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d12partitionVarsT structure.
|
|
func d12initParVars(parVars *d12partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d12notTaken
|
|
}
|
|
}
|
|
|
|
func d12pickSeeds(parVars *d12partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d12maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d12calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d12combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d12calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d12classify(seed0, 0, parVars)
|
|
d12classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d12classify(index, group int, parVars *d12partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d12combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d12calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d12rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d12removeRect provides for eliminating the root.
|
|
func d12removeRect(rect *d12rectT, id interface{}, root **d12nodeT) bool {
|
|
var reInsertList *d12listNodeT
|
|
|
|
if !d12removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d12insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d12removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d12removeRectRec(rect *d12rectT, id interface{}, node *d12nodeT, listNode **d12listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d12overlap(*rect, node.branch[index].rect) {
|
|
if !d12removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d12minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d12nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d12reInsert(node.branch[index].child, listNode)
|
|
d12disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d12disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d12overlap.
|
|
func d12overlap(rectA, rectB d12rectT) bool {
|
|
for index := 0; index < d12numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d12reInsert(node *d12nodeT, listNode **d12listNodeT) {
|
|
newListNode := &d12listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d12search in an index tree or subtree for all data retangles that d12overlap the argument rectangle.
|
|
func d12search(node *d12nodeT, rect d12rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d12overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d12search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d12overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d13fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d13fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d13numDims = 13
|
|
d13maxNodes = 8
|
|
d13minNodes = d13maxNodes / 2
|
|
d13useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d13unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d13numDims]
|
|
|
|
type d13RTree struct {
|
|
root *d13nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d13rectT struct {
|
|
min [d13numDims]float64 ///< Min dimensions of bounding box
|
|
max [d13numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d13branchT struct {
|
|
rect d13rectT ///< Bounds
|
|
child *d13nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d13nodeT for each branch level
|
|
type d13nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d13maxNodes]d13branchT ///< Branch
|
|
}
|
|
|
|
func (node *d13nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d13nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d13listNodeT struct {
|
|
next *d13listNodeT ///< Next in list
|
|
node *d13nodeT ///< Node
|
|
}
|
|
|
|
const d13notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d13partitionVarsT struct {
|
|
partition [d13maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d13rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d13maxNodes + 1]d13branchT
|
|
branchCount int
|
|
coverSplit d13rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d13New() *d13RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d13RTree{
|
|
root: &d13nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d13RTree) Insert(min, max [d13numDims]float64, dataId interface{}) {
|
|
var branch d13branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d13numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d13insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d13RTree) Remove(min, max [d13numDims]float64, dataId interface{}) {
|
|
var rect d13rectT
|
|
for axis := 0; axis < d13numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d13removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d13search rectangle
|
|
/// \param a_min Min of d13search bounding rect
|
|
/// \param a_max Max of d13search bounding rect
|
|
/// \param a_searchResult d13search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d13RTree) Search(min, max [d13numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d13rectT
|
|
for axis := 0; axis < d13numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d13search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d13RTree) Count() int {
|
|
var count int
|
|
d13countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d13RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d13nodeT{}
|
|
}
|
|
|
|
func d13countRec(node *d13nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d13countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d13insertRectRec(branch *d13branchT, node *d13nodeT, newNode **d13nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d13nodeT
|
|
//var newBranch d13branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d13pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d13insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d13combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d13nodeCover(node.branch[index].child)
|
|
var newBranch d13branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d13nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d13addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d13addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d13insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d13insertRect(branch *d13branchT, root **d13nodeT, level int) bool {
|
|
var newNode *d13nodeT
|
|
|
|
if d13insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d13nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d13branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d13nodeCover(*root)
|
|
newBranch.child = *root
|
|
d13addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d13nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d13addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d13nodeCover(node *d13nodeT) d13rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d13combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d13addBranch(branch *d13branchT, node *d13nodeT, newNode **d13nodeT) bool {
|
|
if node.count < d13maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d13splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d13disconnectBranch(node *d13nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d13pickBranch(rect *d13rectT, node *d13nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d13rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d13calcRectVolume(curRect)
|
|
tempRect = d13combineRect(rect, curRect)
|
|
increase = d13calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d13combineRect(rectA, rectB *d13rectT) d13rectT {
|
|
var newRect d13rectT
|
|
|
|
for index := 0; index < d13numDims; index++ {
|
|
newRect.min[index] = d13fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d13fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d13splitNode(node *d13nodeT, branch *d13branchT, newNode **d13nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d13partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d13getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d13choosePartition(parVars, d13minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d13nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d13loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d13rectVolume(rect *d13rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d13numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d13rectT
|
|
func d13rectSphericalVolume(rect *d13rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d13numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d13numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d13unitSphereVolume)
|
|
} else if d13numDims == 4 {
|
|
return (radius * radius * radius * radius * d13unitSphereVolume)
|
|
} else if d13numDims == 3 {
|
|
return (radius * radius * radius * d13unitSphereVolume)
|
|
} else if d13numDims == 2 {
|
|
return (radius * radius * d13unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d13numDims) * d13unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d13calcRectVolume(rect *d13rectT) float64 {
|
|
if d13useSphericalVolume {
|
|
return d13rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d13rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d13getBranches(node *d13nodeT, branch *d13branchT, parVars *d13partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d13maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d13maxNodes] = *branch
|
|
parVars.branchCount = d13maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d13maxNodes+1; index++ {
|
|
parVars.coverSplit = d13combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d13calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d13choosePartition(parVars *d13partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d13initParVars(parVars, parVars.branchCount, minFill)
|
|
d13pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d13notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d13combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d13combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d13calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d13calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d13classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d13notTaken == parVars.partition[index] {
|
|
d13classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d13loadNodes(nodeA, nodeB *d13nodeT, parVars *d13partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d13nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d13addBranch here will not cause a node split.
|
|
d13addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d13partitionVarsT structure.
|
|
func d13initParVars(parVars *d13partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d13notTaken
|
|
}
|
|
}
|
|
|
|
func d13pickSeeds(parVars *d13partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d13maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d13calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d13combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d13calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d13classify(seed0, 0, parVars)
|
|
d13classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d13classify(index, group int, parVars *d13partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d13combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d13calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d13rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d13removeRect provides for eliminating the root.
|
|
func d13removeRect(rect *d13rectT, id interface{}, root **d13nodeT) bool {
|
|
var reInsertList *d13listNodeT
|
|
|
|
if !d13removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d13insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d13removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d13removeRectRec(rect *d13rectT, id interface{}, node *d13nodeT, listNode **d13listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d13overlap(*rect, node.branch[index].rect) {
|
|
if !d13removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d13minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d13nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d13reInsert(node.branch[index].child, listNode)
|
|
d13disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d13disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d13overlap.
|
|
func d13overlap(rectA, rectB d13rectT) bool {
|
|
for index := 0; index < d13numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d13reInsert(node *d13nodeT, listNode **d13listNodeT) {
|
|
newListNode := &d13listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d13search in an index tree or subtree for all data retangles that d13overlap the argument rectangle.
|
|
func d13search(node *d13nodeT, rect d13rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d13overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d13search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d13overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d14fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d14fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d14numDims = 14
|
|
d14maxNodes = 8
|
|
d14minNodes = d14maxNodes / 2
|
|
d14useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d14unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d14numDims]
|
|
|
|
type d14RTree struct {
|
|
root *d14nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d14rectT struct {
|
|
min [d14numDims]float64 ///< Min dimensions of bounding box
|
|
max [d14numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d14branchT struct {
|
|
rect d14rectT ///< Bounds
|
|
child *d14nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d14nodeT for each branch level
|
|
type d14nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d14maxNodes]d14branchT ///< Branch
|
|
}
|
|
|
|
func (node *d14nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d14nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d14listNodeT struct {
|
|
next *d14listNodeT ///< Next in list
|
|
node *d14nodeT ///< Node
|
|
}
|
|
|
|
const d14notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d14partitionVarsT struct {
|
|
partition [d14maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d14rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d14maxNodes + 1]d14branchT
|
|
branchCount int
|
|
coverSplit d14rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d14New() *d14RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d14RTree{
|
|
root: &d14nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d14RTree) Insert(min, max [d14numDims]float64, dataId interface{}) {
|
|
var branch d14branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d14numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d14insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d14RTree) Remove(min, max [d14numDims]float64, dataId interface{}) {
|
|
var rect d14rectT
|
|
for axis := 0; axis < d14numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d14removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d14search rectangle
|
|
/// \param a_min Min of d14search bounding rect
|
|
/// \param a_max Max of d14search bounding rect
|
|
/// \param a_searchResult d14search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d14RTree) Search(min, max [d14numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d14rectT
|
|
for axis := 0; axis < d14numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d14search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d14RTree) Count() int {
|
|
var count int
|
|
d14countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d14RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d14nodeT{}
|
|
}
|
|
|
|
func d14countRec(node *d14nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d14countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d14insertRectRec(branch *d14branchT, node *d14nodeT, newNode **d14nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d14nodeT
|
|
//var newBranch d14branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d14pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d14insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d14combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d14nodeCover(node.branch[index].child)
|
|
var newBranch d14branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d14nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d14addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d14addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d14insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d14insertRect(branch *d14branchT, root **d14nodeT, level int) bool {
|
|
var newNode *d14nodeT
|
|
|
|
if d14insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d14nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d14branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d14nodeCover(*root)
|
|
newBranch.child = *root
|
|
d14addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d14nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d14addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d14nodeCover(node *d14nodeT) d14rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d14combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d14addBranch(branch *d14branchT, node *d14nodeT, newNode **d14nodeT) bool {
|
|
if node.count < d14maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d14splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d14disconnectBranch(node *d14nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d14pickBranch(rect *d14rectT, node *d14nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d14rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d14calcRectVolume(curRect)
|
|
tempRect = d14combineRect(rect, curRect)
|
|
increase = d14calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d14combineRect(rectA, rectB *d14rectT) d14rectT {
|
|
var newRect d14rectT
|
|
|
|
for index := 0; index < d14numDims; index++ {
|
|
newRect.min[index] = d14fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d14fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d14splitNode(node *d14nodeT, branch *d14branchT, newNode **d14nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d14partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d14getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d14choosePartition(parVars, d14minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d14nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d14loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d14rectVolume(rect *d14rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d14numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d14rectT
|
|
func d14rectSphericalVolume(rect *d14rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d14numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d14numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d14unitSphereVolume)
|
|
} else if d14numDims == 4 {
|
|
return (radius * radius * radius * radius * d14unitSphereVolume)
|
|
} else if d14numDims == 3 {
|
|
return (radius * radius * radius * d14unitSphereVolume)
|
|
} else if d14numDims == 2 {
|
|
return (radius * radius * d14unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d14numDims) * d14unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d14calcRectVolume(rect *d14rectT) float64 {
|
|
if d14useSphericalVolume {
|
|
return d14rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d14rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d14getBranches(node *d14nodeT, branch *d14branchT, parVars *d14partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d14maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d14maxNodes] = *branch
|
|
parVars.branchCount = d14maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d14maxNodes+1; index++ {
|
|
parVars.coverSplit = d14combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d14calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d14choosePartition(parVars *d14partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d14initParVars(parVars, parVars.branchCount, minFill)
|
|
d14pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d14notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d14combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d14combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d14calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d14calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d14classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d14notTaken == parVars.partition[index] {
|
|
d14classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d14loadNodes(nodeA, nodeB *d14nodeT, parVars *d14partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d14nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d14addBranch here will not cause a node split.
|
|
d14addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d14partitionVarsT structure.
|
|
func d14initParVars(parVars *d14partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d14notTaken
|
|
}
|
|
}
|
|
|
|
func d14pickSeeds(parVars *d14partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d14maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d14calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d14combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d14calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d14classify(seed0, 0, parVars)
|
|
d14classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d14classify(index, group int, parVars *d14partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d14combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d14calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d14rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d14removeRect provides for eliminating the root.
|
|
func d14removeRect(rect *d14rectT, id interface{}, root **d14nodeT) bool {
|
|
var reInsertList *d14listNodeT
|
|
|
|
if !d14removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d14insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d14removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d14removeRectRec(rect *d14rectT, id interface{}, node *d14nodeT, listNode **d14listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d14overlap(*rect, node.branch[index].rect) {
|
|
if !d14removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d14minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d14nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d14reInsert(node.branch[index].child, listNode)
|
|
d14disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d14disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d14overlap.
|
|
func d14overlap(rectA, rectB d14rectT) bool {
|
|
for index := 0; index < d14numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d14reInsert(node *d14nodeT, listNode **d14listNodeT) {
|
|
newListNode := &d14listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d14search in an index tree or subtree for all data retangles that d14overlap the argument rectangle.
|
|
func d14search(node *d14nodeT, rect d14rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d14overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d14search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d14overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d15fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d15fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d15numDims = 15
|
|
d15maxNodes = 8
|
|
d15minNodes = d15maxNodes / 2
|
|
d15useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d15unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d15numDims]
|
|
|
|
type d15RTree struct {
|
|
root *d15nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d15rectT struct {
|
|
min [d15numDims]float64 ///< Min dimensions of bounding box
|
|
max [d15numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d15branchT struct {
|
|
rect d15rectT ///< Bounds
|
|
child *d15nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d15nodeT for each branch level
|
|
type d15nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d15maxNodes]d15branchT ///< Branch
|
|
}
|
|
|
|
func (node *d15nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d15nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d15listNodeT struct {
|
|
next *d15listNodeT ///< Next in list
|
|
node *d15nodeT ///< Node
|
|
}
|
|
|
|
const d15notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d15partitionVarsT struct {
|
|
partition [d15maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d15rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d15maxNodes + 1]d15branchT
|
|
branchCount int
|
|
coverSplit d15rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d15New() *d15RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d15RTree{
|
|
root: &d15nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d15RTree) Insert(min, max [d15numDims]float64, dataId interface{}) {
|
|
var branch d15branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d15numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d15insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d15RTree) Remove(min, max [d15numDims]float64, dataId interface{}) {
|
|
var rect d15rectT
|
|
for axis := 0; axis < d15numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d15removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d15search rectangle
|
|
/// \param a_min Min of d15search bounding rect
|
|
/// \param a_max Max of d15search bounding rect
|
|
/// \param a_searchResult d15search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d15RTree) Search(min, max [d15numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d15rectT
|
|
for axis := 0; axis < d15numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d15search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d15RTree) Count() int {
|
|
var count int
|
|
d15countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d15RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d15nodeT{}
|
|
}
|
|
|
|
func d15countRec(node *d15nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d15countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d15insertRectRec(branch *d15branchT, node *d15nodeT, newNode **d15nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d15nodeT
|
|
//var newBranch d15branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d15pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d15insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d15combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d15nodeCover(node.branch[index].child)
|
|
var newBranch d15branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d15nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d15addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d15addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d15insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d15insertRect(branch *d15branchT, root **d15nodeT, level int) bool {
|
|
var newNode *d15nodeT
|
|
|
|
if d15insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d15nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d15branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d15nodeCover(*root)
|
|
newBranch.child = *root
|
|
d15addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d15nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d15addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d15nodeCover(node *d15nodeT) d15rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d15combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d15addBranch(branch *d15branchT, node *d15nodeT, newNode **d15nodeT) bool {
|
|
if node.count < d15maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d15splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d15disconnectBranch(node *d15nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d15pickBranch(rect *d15rectT, node *d15nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d15rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d15calcRectVolume(curRect)
|
|
tempRect = d15combineRect(rect, curRect)
|
|
increase = d15calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d15combineRect(rectA, rectB *d15rectT) d15rectT {
|
|
var newRect d15rectT
|
|
|
|
for index := 0; index < d15numDims; index++ {
|
|
newRect.min[index] = d15fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d15fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d15splitNode(node *d15nodeT, branch *d15branchT, newNode **d15nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d15partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d15getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d15choosePartition(parVars, d15minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d15nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d15loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d15rectVolume(rect *d15rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d15numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d15rectT
|
|
func d15rectSphericalVolume(rect *d15rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d15numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d15numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d15unitSphereVolume)
|
|
} else if d15numDims == 4 {
|
|
return (radius * radius * radius * radius * d15unitSphereVolume)
|
|
} else if d15numDims == 3 {
|
|
return (radius * radius * radius * d15unitSphereVolume)
|
|
} else if d15numDims == 2 {
|
|
return (radius * radius * d15unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d15numDims) * d15unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d15calcRectVolume(rect *d15rectT) float64 {
|
|
if d15useSphericalVolume {
|
|
return d15rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d15rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d15getBranches(node *d15nodeT, branch *d15branchT, parVars *d15partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d15maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d15maxNodes] = *branch
|
|
parVars.branchCount = d15maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d15maxNodes+1; index++ {
|
|
parVars.coverSplit = d15combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d15calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d15choosePartition(parVars *d15partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d15initParVars(parVars, parVars.branchCount, minFill)
|
|
d15pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d15notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d15combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d15combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d15calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d15calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d15classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d15notTaken == parVars.partition[index] {
|
|
d15classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d15loadNodes(nodeA, nodeB *d15nodeT, parVars *d15partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d15nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d15addBranch here will not cause a node split.
|
|
d15addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d15partitionVarsT structure.
|
|
func d15initParVars(parVars *d15partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d15notTaken
|
|
}
|
|
}
|
|
|
|
func d15pickSeeds(parVars *d15partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d15maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d15calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d15combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d15calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d15classify(seed0, 0, parVars)
|
|
d15classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d15classify(index, group int, parVars *d15partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d15combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d15calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d15rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d15removeRect provides for eliminating the root.
|
|
func d15removeRect(rect *d15rectT, id interface{}, root **d15nodeT) bool {
|
|
var reInsertList *d15listNodeT
|
|
|
|
if !d15removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d15insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d15removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d15removeRectRec(rect *d15rectT, id interface{}, node *d15nodeT, listNode **d15listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d15overlap(*rect, node.branch[index].rect) {
|
|
if !d15removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d15minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d15nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d15reInsert(node.branch[index].child, listNode)
|
|
d15disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d15disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d15overlap.
|
|
func d15overlap(rectA, rectB d15rectT) bool {
|
|
for index := 0; index < d15numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d15reInsert(node *d15nodeT, listNode **d15listNodeT) {
|
|
newListNode := &d15listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d15search in an index tree or subtree for all data retangles that d15overlap the argument rectangle.
|
|
func d15search(node *d15nodeT, rect d15rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d15overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d15search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d15overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d16fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d16fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d16numDims = 16
|
|
d16maxNodes = 8
|
|
d16minNodes = d16maxNodes / 2
|
|
d16useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d16unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d16numDims]
|
|
|
|
type d16RTree struct {
|
|
root *d16nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d16rectT struct {
|
|
min [d16numDims]float64 ///< Min dimensions of bounding box
|
|
max [d16numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d16branchT struct {
|
|
rect d16rectT ///< Bounds
|
|
child *d16nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d16nodeT for each branch level
|
|
type d16nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d16maxNodes]d16branchT ///< Branch
|
|
}
|
|
|
|
func (node *d16nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d16nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d16listNodeT struct {
|
|
next *d16listNodeT ///< Next in list
|
|
node *d16nodeT ///< Node
|
|
}
|
|
|
|
const d16notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d16partitionVarsT struct {
|
|
partition [d16maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d16rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d16maxNodes + 1]d16branchT
|
|
branchCount int
|
|
coverSplit d16rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d16New() *d16RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d16RTree{
|
|
root: &d16nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d16RTree) Insert(min, max [d16numDims]float64, dataId interface{}) {
|
|
var branch d16branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d16numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d16insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d16RTree) Remove(min, max [d16numDims]float64, dataId interface{}) {
|
|
var rect d16rectT
|
|
for axis := 0; axis < d16numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d16removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d16search rectangle
|
|
/// \param a_min Min of d16search bounding rect
|
|
/// \param a_max Max of d16search bounding rect
|
|
/// \param a_searchResult d16search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d16RTree) Search(min, max [d16numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d16rectT
|
|
for axis := 0; axis < d16numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d16search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d16RTree) Count() int {
|
|
var count int
|
|
d16countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d16RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d16nodeT{}
|
|
}
|
|
|
|
func d16countRec(node *d16nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d16countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d16insertRectRec(branch *d16branchT, node *d16nodeT, newNode **d16nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d16nodeT
|
|
//var newBranch d16branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d16pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d16insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d16combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d16nodeCover(node.branch[index].child)
|
|
var newBranch d16branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d16nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d16addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d16addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d16insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d16insertRect(branch *d16branchT, root **d16nodeT, level int) bool {
|
|
var newNode *d16nodeT
|
|
|
|
if d16insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d16nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d16branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d16nodeCover(*root)
|
|
newBranch.child = *root
|
|
d16addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d16nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d16addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d16nodeCover(node *d16nodeT) d16rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d16combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d16addBranch(branch *d16branchT, node *d16nodeT, newNode **d16nodeT) bool {
|
|
if node.count < d16maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d16splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d16disconnectBranch(node *d16nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d16pickBranch(rect *d16rectT, node *d16nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d16rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d16calcRectVolume(curRect)
|
|
tempRect = d16combineRect(rect, curRect)
|
|
increase = d16calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d16combineRect(rectA, rectB *d16rectT) d16rectT {
|
|
var newRect d16rectT
|
|
|
|
for index := 0; index < d16numDims; index++ {
|
|
newRect.min[index] = d16fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d16fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d16splitNode(node *d16nodeT, branch *d16branchT, newNode **d16nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d16partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d16getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d16choosePartition(parVars, d16minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d16nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d16loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d16rectVolume(rect *d16rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d16numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d16rectT
|
|
func d16rectSphericalVolume(rect *d16rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d16numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d16numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d16unitSphereVolume)
|
|
} else if d16numDims == 4 {
|
|
return (radius * radius * radius * radius * d16unitSphereVolume)
|
|
} else if d16numDims == 3 {
|
|
return (radius * radius * radius * d16unitSphereVolume)
|
|
} else if d16numDims == 2 {
|
|
return (radius * radius * d16unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d16numDims) * d16unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d16calcRectVolume(rect *d16rectT) float64 {
|
|
if d16useSphericalVolume {
|
|
return d16rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d16rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d16getBranches(node *d16nodeT, branch *d16branchT, parVars *d16partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d16maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d16maxNodes] = *branch
|
|
parVars.branchCount = d16maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d16maxNodes+1; index++ {
|
|
parVars.coverSplit = d16combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d16calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d16choosePartition(parVars *d16partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d16initParVars(parVars, parVars.branchCount, minFill)
|
|
d16pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d16notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d16combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d16combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d16calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d16calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d16classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d16notTaken == parVars.partition[index] {
|
|
d16classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d16loadNodes(nodeA, nodeB *d16nodeT, parVars *d16partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d16nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d16addBranch here will not cause a node split.
|
|
d16addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d16partitionVarsT structure.
|
|
func d16initParVars(parVars *d16partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d16notTaken
|
|
}
|
|
}
|
|
|
|
func d16pickSeeds(parVars *d16partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d16maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d16calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d16combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d16calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d16classify(seed0, 0, parVars)
|
|
d16classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d16classify(index, group int, parVars *d16partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d16combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d16calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d16rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d16removeRect provides for eliminating the root.
|
|
func d16removeRect(rect *d16rectT, id interface{}, root **d16nodeT) bool {
|
|
var reInsertList *d16listNodeT
|
|
|
|
if !d16removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d16insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d16removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d16removeRectRec(rect *d16rectT, id interface{}, node *d16nodeT, listNode **d16listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d16overlap(*rect, node.branch[index].rect) {
|
|
if !d16removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d16minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d16nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d16reInsert(node.branch[index].child, listNode)
|
|
d16disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d16disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d16overlap.
|
|
func d16overlap(rectA, rectB d16rectT) bool {
|
|
for index := 0; index < d16numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d16reInsert(node *d16nodeT, listNode **d16listNodeT) {
|
|
newListNode := &d16listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d16search in an index tree or subtree for all data retangles that d16overlap the argument rectangle.
|
|
func d16search(node *d16nodeT, rect d16rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d16overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d16search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d16overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d17fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d17fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d17numDims = 17
|
|
d17maxNodes = 8
|
|
d17minNodes = d17maxNodes / 2
|
|
d17useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d17unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d17numDims]
|
|
|
|
type d17RTree struct {
|
|
root *d17nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d17rectT struct {
|
|
min [d17numDims]float64 ///< Min dimensions of bounding box
|
|
max [d17numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d17branchT struct {
|
|
rect d17rectT ///< Bounds
|
|
child *d17nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d17nodeT for each branch level
|
|
type d17nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d17maxNodes]d17branchT ///< Branch
|
|
}
|
|
|
|
func (node *d17nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d17nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d17listNodeT struct {
|
|
next *d17listNodeT ///< Next in list
|
|
node *d17nodeT ///< Node
|
|
}
|
|
|
|
const d17notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d17partitionVarsT struct {
|
|
partition [d17maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d17rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d17maxNodes + 1]d17branchT
|
|
branchCount int
|
|
coverSplit d17rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d17New() *d17RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d17RTree{
|
|
root: &d17nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d17RTree) Insert(min, max [d17numDims]float64, dataId interface{}) {
|
|
var branch d17branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d17numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d17insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d17RTree) Remove(min, max [d17numDims]float64, dataId interface{}) {
|
|
var rect d17rectT
|
|
for axis := 0; axis < d17numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d17removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d17search rectangle
|
|
/// \param a_min Min of d17search bounding rect
|
|
/// \param a_max Max of d17search bounding rect
|
|
/// \param a_searchResult d17search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d17RTree) Search(min, max [d17numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d17rectT
|
|
for axis := 0; axis < d17numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d17search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d17RTree) Count() int {
|
|
var count int
|
|
d17countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d17RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d17nodeT{}
|
|
}
|
|
|
|
func d17countRec(node *d17nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d17countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d17insertRectRec(branch *d17branchT, node *d17nodeT, newNode **d17nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d17nodeT
|
|
//var newBranch d17branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d17pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d17insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d17combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d17nodeCover(node.branch[index].child)
|
|
var newBranch d17branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d17nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d17addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d17addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d17insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d17insertRect(branch *d17branchT, root **d17nodeT, level int) bool {
|
|
var newNode *d17nodeT
|
|
|
|
if d17insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d17nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d17branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d17nodeCover(*root)
|
|
newBranch.child = *root
|
|
d17addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d17nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d17addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d17nodeCover(node *d17nodeT) d17rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d17combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d17addBranch(branch *d17branchT, node *d17nodeT, newNode **d17nodeT) bool {
|
|
if node.count < d17maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d17splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d17disconnectBranch(node *d17nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d17pickBranch(rect *d17rectT, node *d17nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d17rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d17calcRectVolume(curRect)
|
|
tempRect = d17combineRect(rect, curRect)
|
|
increase = d17calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d17combineRect(rectA, rectB *d17rectT) d17rectT {
|
|
var newRect d17rectT
|
|
|
|
for index := 0; index < d17numDims; index++ {
|
|
newRect.min[index] = d17fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d17fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d17splitNode(node *d17nodeT, branch *d17branchT, newNode **d17nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d17partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d17getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d17choosePartition(parVars, d17minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d17nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d17loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d17rectVolume(rect *d17rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d17numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d17rectT
|
|
func d17rectSphericalVolume(rect *d17rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d17numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d17numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d17unitSphereVolume)
|
|
} else if d17numDims == 4 {
|
|
return (radius * radius * radius * radius * d17unitSphereVolume)
|
|
} else if d17numDims == 3 {
|
|
return (radius * radius * radius * d17unitSphereVolume)
|
|
} else if d17numDims == 2 {
|
|
return (radius * radius * d17unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d17numDims) * d17unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d17calcRectVolume(rect *d17rectT) float64 {
|
|
if d17useSphericalVolume {
|
|
return d17rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d17rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d17getBranches(node *d17nodeT, branch *d17branchT, parVars *d17partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d17maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d17maxNodes] = *branch
|
|
parVars.branchCount = d17maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d17maxNodes+1; index++ {
|
|
parVars.coverSplit = d17combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d17calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d17choosePartition(parVars *d17partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d17initParVars(parVars, parVars.branchCount, minFill)
|
|
d17pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d17notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d17combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d17combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d17calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d17calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d17classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d17notTaken == parVars.partition[index] {
|
|
d17classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d17loadNodes(nodeA, nodeB *d17nodeT, parVars *d17partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d17nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d17addBranch here will not cause a node split.
|
|
d17addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d17partitionVarsT structure.
|
|
func d17initParVars(parVars *d17partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d17notTaken
|
|
}
|
|
}
|
|
|
|
func d17pickSeeds(parVars *d17partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d17maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d17calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d17combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d17calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d17classify(seed0, 0, parVars)
|
|
d17classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d17classify(index, group int, parVars *d17partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d17combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d17calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d17rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d17removeRect provides for eliminating the root.
|
|
func d17removeRect(rect *d17rectT, id interface{}, root **d17nodeT) bool {
|
|
var reInsertList *d17listNodeT
|
|
|
|
if !d17removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d17insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d17removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d17removeRectRec(rect *d17rectT, id interface{}, node *d17nodeT, listNode **d17listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d17overlap(*rect, node.branch[index].rect) {
|
|
if !d17removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d17minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d17nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d17reInsert(node.branch[index].child, listNode)
|
|
d17disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d17disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d17overlap.
|
|
func d17overlap(rectA, rectB d17rectT) bool {
|
|
for index := 0; index < d17numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d17reInsert(node *d17nodeT, listNode **d17listNodeT) {
|
|
newListNode := &d17listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d17search in an index tree or subtree for all data retangles that d17overlap the argument rectangle.
|
|
func d17search(node *d17nodeT, rect d17rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d17overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d17search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d17overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d18fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d18fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d18numDims = 18
|
|
d18maxNodes = 8
|
|
d18minNodes = d18maxNodes / 2
|
|
d18useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d18unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d18numDims]
|
|
|
|
type d18RTree struct {
|
|
root *d18nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d18rectT struct {
|
|
min [d18numDims]float64 ///< Min dimensions of bounding box
|
|
max [d18numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d18branchT struct {
|
|
rect d18rectT ///< Bounds
|
|
child *d18nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d18nodeT for each branch level
|
|
type d18nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d18maxNodes]d18branchT ///< Branch
|
|
}
|
|
|
|
func (node *d18nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d18nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d18listNodeT struct {
|
|
next *d18listNodeT ///< Next in list
|
|
node *d18nodeT ///< Node
|
|
}
|
|
|
|
const d18notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d18partitionVarsT struct {
|
|
partition [d18maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d18rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d18maxNodes + 1]d18branchT
|
|
branchCount int
|
|
coverSplit d18rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d18New() *d18RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d18RTree{
|
|
root: &d18nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d18RTree) Insert(min, max [d18numDims]float64, dataId interface{}) {
|
|
var branch d18branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d18numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d18insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d18RTree) Remove(min, max [d18numDims]float64, dataId interface{}) {
|
|
var rect d18rectT
|
|
for axis := 0; axis < d18numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d18removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d18search rectangle
|
|
/// \param a_min Min of d18search bounding rect
|
|
/// \param a_max Max of d18search bounding rect
|
|
/// \param a_searchResult d18search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d18RTree) Search(min, max [d18numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d18rectT
|
|
for axis := 0; axis < d18numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d18search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d18RTree) Count() int {
|
|
var count int
|
|
d18countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d18RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d18nodeT{}
|
|
}
|
|
|
|
func d18countRec(node *d18nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d18countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d18insertRectRec(branch *d18branchT, node *d18nodeT, newNode **d18nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d18nodeT
|
|
//var newBranch d18branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d18pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d18insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d18combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d18nodeCover(node.branch[index].child)
|
|
var newBranch d18branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d18nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d18addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d18addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d18insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d18insertRect(branch *d18branchT, root **d18nodeT, level int) bool {
|
|
var newNode *d18nodeT
|
|
|
|
if d18insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d18nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d18branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d18nodeCover(*root)
|
|
newBranch.child = *root
|
|
d18addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d18nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d18addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d18nodeCover(node *d18nodeT) d18rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d18combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d18addBranch(branch *d18branchT, node *d18nodeT, newNode **d18nodeT) bool {
|
|
if node.count < d18maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d18splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d18disconnectBranch(node *d18nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d18pickBranch(rect *d18rectT, node *d18nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d18rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d18calcRectVolume(curRect)
|
|
tempRect = d18combineRect(rect, curRect)
|
|
increase = d18calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d18combineRect(rectA, rectB *d18rectT) d18rectT {
|
|
var newRect d18rectT
|
|
|
|
for index := 0; index < d18numDims; index++ {
|
|
newRect.min[index] = d18fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d18fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d18splitNode(node *d18nodeT, branch *d18branchT, newNode **d18nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d18partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d18getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d18choosePartition(parVars, d18minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d18nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d18loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d18rectVolume(rect *d18rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d18numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d18rectT
|
|
func d18rectSphericalVolume(rect *d18rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d18numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d18numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d18unitSphereVolume)
|
|
} else if d18numDims == 4 {
|
|
return (radius * radius * radius * radius * d18unitSphereVolume)
|
|
} else if d18numDims == 3 {
|
|
return (radius * radius * radius * d18unitSphereVolume)
|
|
} else if d18numDims == 2 {
|
|
return (radius * radius * d18unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d18numDims) * d18unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d18calcRectVolume(rect *d18rectT) float64 {
|
|
if d18useSphericalVolume {
|
|
return d18rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d18rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d18getBranches(node *d18nodeT, branch *d18branchT, parVars *d18partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d18maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d18maxNodes] = *branch
|
|
parVars.branchCount = d18maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d18maxNodes+1; index++ {
|
|
parVars.coverSplit = d18combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d18calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d18choosePartition(parVars *d18partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d18initParVars(parVars, parVars.branchCount, minFill)
|
|
d18pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d18notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d18combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d18combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d18calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d18calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d18classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d18notTaken == parVars.partition[index] {
|
|
d18classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d18loadNodes(nodeA, nodeB *d18nodeT, parVars *d18partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d18nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d18addBranch here will not cause a node split.
|
|
d18addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d18partitionVarsT structure.
|
|
func d18initParVars(parVars *d18partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d18notTaken
|
|
}
|
|
}
|
|
|
|
func d18pickSeeds(parVars *d18partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d18maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d18calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d18combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d18calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d18classify(seed0, 0, parVars)
|
|
d18classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d18classify(index, group int, parVars *d18partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d18combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d18calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d18rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d18removeRect provides for eliminating the root.
|
|
func d18removeRect(rect *d18rectT, id interface{}, root **d18nodeT) bool {
|
|
var reInsertList *d18listNodeT
|
|
|
|
if !d18removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d18insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d18removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d18removeRectRec(rect *d18rectT, id interface{}, node *d18nodeT, listNode **d18listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d18overlap(*rect, node.branch[index].rect) {
|
|
if !d18removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d18minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d18nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d18reInsert(node.branch[index].child, listNode)
|
|
d18disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d18disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d18overlap.
|
|
func d18overlap(rectA, rectB d18rectT) bool {
|
|
for index := 0; index < d18numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d18reInsert(node *d18nodeT, listNode **d18listNodeT) {
|
|
newListNode := &d18listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d18search in an index tree or subtree for all data retangles that d18overlap the argument rectangle.
|
|
func d18search(node *d18nodeT, rect d18rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d18overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d18search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d18overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d19fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d19fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d19numDims = 19
|
|
d19maxNodes = 8
|
|
d19minNodes = d19maxNodes / 2
|
|
d19useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d19unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d19numDims]
|
|
|
|
type d19RTree struct {
|
|
root *d19nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d19rectT struct {
|
|
min [d19numDims]float64 ///< Min dimensions of bounding box
|
|
max [d19numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d19branchT struct {
|
|
rect d19rectT ///< Bounds
|
|
child *d19nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d19nodeT for each branch level
|
|
type d19nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d19maxNodes]d19branchT ///< Branch
|
|
}
|
|
|
|
func (node *d19nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d19nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d19listNodeT struct {
|
|
next *d19listNodeT ///< Next in list
|
|
node *d19nodeT ///< Node
|
|
}
|
|
|
|
const d19notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d19partitionVarsT struct {
|
|
partition [d19maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d19rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d19maxNodes + 1]d19branchT
|
|
branchCount int
|
|
coverSplit d19rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d19New() *d19RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d19RTree{
|
|
root: &d19nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d19RTree) Insert(min, max [d19numDims]float64, dataId interface{}) {
|
|
var branch d19branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d19numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d19insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d19RTree) Remove(min, max [d19numDims]float64, dataId interface{}) {
|
|
var rect d19rectT
|
|
for axis := 0; axis < d19numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d19removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d19search rectangle
|
|
/// \param a_min Min of d19search bounding rect
|
|
/// \param a_max Max of d19search bounding rect
|
|
/// \param a_searchResult d19search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d19RTree) Search(min, max [d19numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d19rectT
|
|
for axis := 0; axis < d19numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d19search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d19RTree) Count() int {
|
|
var count int
|
|
d19countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d19RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d19nodeT{}
|
|
}
|
|
|
|
func d19countRec(node *d19nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d19countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d19insertRectRec(branch *d19branchT, node *d19nodeT, newNode **d19nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d19nodeT
|
|
//var newBranch d19branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d19pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d19insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d19combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d19nodeCover(node.branch[index].child)
|
|
var newBranch d19branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d19nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d19addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d19addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d19insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d19insertRect(branch *d19branchT, root **d19nodeT, level int) bool {
|
|
var newNode *d19nodeT
|
|
|
|
if d19insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d19nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d19branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d19nodeCover(*root)
|
|
newBranch.child = *root
|
|
d19addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d19nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d19addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d19nodeCover(node *d19nodeT) d19rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d19combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d19addBranch(branch *d19branchT, node *d19nodeT, newNode **d19nodeT) bool {
|
|
if node.count < d19maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d19splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d19disconnectBranch(node *d19nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d19pickBranch(rect *d19rectT, node *d19nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d19rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d19calcRectVolume(curRect)
|
|
tempRect = d19combineRect(rect, curRect)
|
|
increase = d19calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d19combineRect(rectA, rectB *d19rectT) d19rectT {
|
|
var newRect d19rectT
|
|
|
|
for index := 0; index < d19numDims; index++ {
|
|
newRect.min[index] = d19fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d19fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d19splitNode(node *d19nodeT, branch *d19branchT, newNode **d19nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d19partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d19getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d19choosePartition(parVars, d19minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d19nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d19loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d19rectVolume(rect *d19rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d19numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d19rectT
|
|
func d19rectSphericalVolume(rect *d19rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d19numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d19numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d19unitSphereVolume)
|
|
} else if d19numDims == 4 {
|
|
return (radius * radius * radius * radius * d19unitSphereVolume)
|
|
} else if d19numDims == 3 {
|
|
return (radius * radius * radius * d19unitSphereVolume)
|
|
} else if d19numDims == 2 {
|
|
return (radius * radius * d19unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d19numDims) * d19unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d19calcRectVolume(rect *d19rectT) float64 {
|
|
if d19useSphericalVolume {
|
|
return d19rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d19rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d19getBranches(node *d19nodeT, branch *d19branchT, parVars *d19partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d19maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d19maxNodes] = *branch
|
|
parVars.branchCount = d19maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d19maxNodes+1; index++ {
|
|
parVars.coverSplit = d19combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d19calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d19choosePartition(parVars *d19partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d19initParVars(parVars, parVars.branchCount, minFill)
|
|
d19pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d19notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d19combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d19combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d19calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d19calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d19classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d19notTaken == parVars.partition[index] {
|
|
d19classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d19loadNodes(nodeA, nodeB *d19nodeT, parVars *d19partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d19nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d19addBranch here will not cause a node split.
|
|
d19addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d19partitionVarsT structure.
|
|
func d19initParVars(parVars *d19partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d19notTaken
|
|
}
|
|
}
|
|
|
|
func d19pickSeeds(parVars *d19partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d19maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d19calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d19combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d19calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d19classify(seed0, 0, parVars)
|
|
d19classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d19classify(index, group int, parVars *d19partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d19combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d19calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d19rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d19removeRect provides for eliminating the root.
|
|
func d19removeRect(rect *d19rectT, id interface{}, root **d19nodeT) bool {
|
|
var reInsertList *d19listNodeT
|
|
|
|
if !d19removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d19insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d19removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d19removeRectRec(rect *d19rectT, id interface{}, node *d19nodeT, listNode **d19listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d19overlap(*rect, node.branch[index].rect) {
|
|
if !d19removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d19minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d19nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d19reInsert(node.branch[index].child, listNode)
|
|
d19disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d19disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d19overlap.
|
|
func d19overlap(rectA, rectB d19rectT) bool {
|
|
for index := 0; index < d19numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d19reInsert(node *d19nodeT, listNode **d19listNodeT) {
|
|
newListNode := &d19listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d19search in an index tree or subtree for all data retangles that d19overlap the argument rectangle.
|
|
func d19search(node *d19nodeT, rect d19rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d19overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d19search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d19overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|
|
|
|
func d20fmin(a, b float64) float64 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
func d20fmax(a, b float64) float64 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
d20numDims = 20
|
|
d20maxNodes = 8
|
|
d20minNodes = d20maxNodes / 2
|
|
d20useSphericalVolume = true // Better split classification, may be slower on some systems
|
|
)
|
|
|
|
var d20unitSphereVolume = []float64{
|
|
0.000000, 2.000000, 3.141593, // Dimension 0,1,2
|
|
4.188790, 4.934802, 5.263789, // Dimension 3,4,5
|
|
5.167713, 4.724766, 4.058712, // Dimension 6,7,8
|
|
3.298509, 2.550164, 1.884104, // Dimension 9,10,11
|
|
1.335263, 0.910629, 0.599265, // Dimension 12,13,14
|
|
0.381443, 0.235331, 0.140981, // Dimension 15,16,17
|
|
0.082146, 0.046622, 0.025807, // Dimension 18,19,20
|
|
}[d20numDims]
|
|
|
|
type d20RTree struct {
|
|
root *d20nodeT ///< Root of tree
|
|
}
|
|
|
|
/// Minimal bounding rectangle (n-dimensional)
|
|
type d20rectT struct {
|
|
min [d20numDims]float64 ///< Min dimensions of bounding box
|
|
max [d20numDims]float64 ///< Max dimensions of bounding box
|
|
}
|
|
|
|
/// May be data or may be another subtree
|
|
/// The parents level determines this.
|
|
/// If the parents level is 0, then this is data
|
|
type d20branchT struct {
|
|
rect d20rectT ///< Bounds
|
|
child *d20nodeT ///< Child node
|
|
data interface{} ///< Data Id or Ptr
|
|
}
|
|
|
|
/// d20nodeT for each branch level
|
|
type d20nodeT struct {
|
|
count int ///< Count
|
|
level int ///< Leaf is zero, others positive
|
|
branch [d20maxNodes]d20branchT ///< Branch
|
|
}
|
|
|
|
func (node *d20nodeT) isInternalNode() bool {
|
|
return (node.level > 0) // Not a leaf, but a internal node
|
|
}
|
|
func (node *d20nodeT) isLeaf() bool {
|
|
return (node.level == 0) // A leaf, contains data
|
|
}
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
type d20listNodeT struct {
|
|
next *d20listNodeT ///< Next in list
|
|
node *d20nodeT ///< Node
|
|
}
|
|
|
|
const d20notTaken = -1 // indicates that position
|
|
|
|
/// Variables for finding a split partition
|
|
type d20partitionVarsT struct {
|
|
partition [d20maxNodes + 1]int
|
|
total int
|
|
minFill int
|
|
count [2]int
|
|
cover [2]d20rectT
|
|
area [2]float64
|
|
|
|
branchBuf [d20maxNodes + 1]d20branchT
|
|
branchCount int
|
|
coverSplit d20rectT
|
|
coverSplitArea float64
|
|
}
|
|
|
|
func d20New() *d20RTree {
|
|
// We only support machine word size simple data type eg. integer index or object pointer.
|
|
// Since we are storing as union with non data branch
|
|
return &d20RTree{
|
|
root: &d20nodeT{},
|
|
}
|
|
}
|
|
|
|
/// Insert entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d20RTree) Insert(min, max [d20numDims]float64, dataId interface{}) {
|
|
var branch d20branchT
|
|
branch.data = dataId
|
|
for axis := 0; axis < d20numDims; axis++ {
|
|
branch.rect.min[axis] = min[axis]
|
|
branch.rect.max[axis] = max[axis]
|
|
}
|
|
d20insertRect(&branch, &tr.root, 0)
|
|
}
|
|
|
|
/// Remove entry
|
|
/// \param a_min Min of bounding rect
|
|
/// \param a_max Max of bounding rect
|
|
/// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed.
|
|
func (tr *d20RTree) Remove(min, max [d20numDims]float64, dataId interface{}) {
|
|
var rect d20rectT
|
|
for axis := 0; axis < d20numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
d20removeRect(&rect, dataId, &tr.root)
|
|
}
|
|
|
|
/// Find all within d20search rectangle
|
|
/// \param a_min Min of d20search bounding rect
|
|
/// \param a_max Max of d20search bounding rect
|
|
/// \param a_searchResult d20search result array. Caller should set grow size. Function will reset, not append to array.
|
|
/// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching
|
|
/// \param a_context User context to pass as parameter to a_resultCallback
|
|
/// \return Returns the number of entries found
|
|
func (tr *d20RTree) Search(min, max [d20numDims]float64, resultCallback func(data interface{}) bool) int {
|
|
var rect d20rectT
|
|
for axis := 0; axis < d20numDims; axis++ {
|
|
rect.min[axis] = min[axis]
|
|
rect.max[axis] = max[axis]
|
|
}
|
|
foundCount, _ := d20search(tr.root, rect, 0, resultCallback)
|
|
return foundCount
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
func (tr *d20RTree) Count() int {
|
|
var count int
|
|
d20countRec(tr.root, &count)
|
|
return count
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
func (tr *d20RTree) RemoveAll() {
|
|
// Delete all existing nodes
|
|
tr.root = &d20nodeT{}
|
|
}
|
|
|
|
func d20countRec(node *d20nodeT, count *int) {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
d20countRec(node.branch[index].child, count)
|
|
}
|
|
} else { // A leaf node
|
|
*count += node.count
|
|
}
|
|
}
|
|
|
|
// Inserts a new data rectangle into the index structure.
|
|
// Recursively descends tree, propagates splits back up.
|
|
// Returns 0 if node was not split. Old node updated.
|
|
// If node was split, returns 1 and sets the pointer pointed to by
|
|
// new_node to point to the new node. Old node updated to become one of two.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
func d20insertRectRec(branch *d20branchT, node *d20nodeT, newNode **d20nodeT, level int) bool {
|
|
// recurse until we reach the correct level for the new record. data records
|
|
// will always be called with a_level == 0 (leaf)
|
|
if node.level > level {
|
|
// Still above level for insertion, go down tree recursively
|
|
var otherNode *d20nodeT
|
|
//var newBranch d20branchT
|
|
|
|
// find the optimal branch for this record
|
|
index := d20pickBranch(&branch.rect, node)
|
|
|
|
// recursively insert this record into the picked branch
|
|
childWasSplit := d20insertRectRec(branch, node.branch[index].child, &otherNode, level)
|
|
|
|
if !childWasSplit {
|
|
// Child was not split. Merge the bounding box of the new record with the
|
|
// existing bounding box
|
|
node.branch[index].rect = d20combineRect(&branch.rect, &(node.branch[index].rect))
|
|
return false
|
|
} else {
|
|
// Child was split. The old branches are now re-partitioned to two nodes
|
|
// so we have to re-calculate the bounding boxes of each node
|
|
node.branch[index].rect = d20nodeCover(node.branch[index].child)
|
|
var newBranch d20branchT
|
|
newBranch.child = otherNode
|
|
newBranch.rect = d20nodeCover(otherNode)
|
|
|
|
// The old node is already a child of a_node. Now add the newly-created
|
|
// node to a_node as well. a_node might be split because of that.
|
|
return d20addBranch(&newBranch, node, newNode)
|
|
}
|
|
} else if node.level == level {
|
|
// We have reached level for insertion. Add rect, split if necessary
|
|
return d20addBranch(branch, node, newNode)
|
|
} else {
|
|
// Should never occur
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Insert a data rectangle into an index structure.
|
|
// d20insertRect provides for splitting the root;
|
|
// returns 1 if root was split, 0 if it was not.
|
|
// The level argument specifies the number of steps up from the leaf
|
|
// level to insert; e.g. a data rectangle goes in at level = 0.
|
|
// InsertRect2 does the recursion.
|
|
//
|
|
func d20insertRect(branch *d20branchT, root **d20nodeT, level int) bool {
|
|
var newNode *d20nodeT
|
|
|
|
if d20insertRectRec(branch, *root, &newNode, level) { // Root split
|
|
|
|
// Grow tree taller and new root
|
|
newRoot := &d20nodeT{}
|
|
newRoot.level = (*root).level + 1
|
|
|
|
var newBranch d20branchT
|
|
|
|
// add old root node as a child of the new root
|
|
newBranch.rect = d20nodeCover(*root)
|
|
newBranch.child = *root
|
|
d20addBranch(&newBranch, newRoot, nil)
|
|
|
|
// add the split node as a child of the new root
|
|
newBranch.rect = d20nodeCover(newNode)
|
|
newBranch.child = newNode
|
|
d20addBranch(&newBranch, newRoot, nil)
|
|
|
|
// set the new root as the root node
|
|
*root = newRoot
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Find the smallest rectangle that includes all rectangles in branches of a node.
|
|
func d20nodeCover(node *d20nodeT) d20rectT {
|
|
rect := node.branch[0].rect
|
|
for index := 1; index < node.count; index++ {
|
|
rect = d20combineRect(&rect, &(node.branch[index].rect))
|
|
}
|
|
return rect
|
|
}
|
|
|
|
// Add a branch to a node. Split the node if necessary.
|
|
// Returns 0 if node not split. Old node updated.
|
|
// Returns 1 if node split, sets *new_node to address of new node.
|
|
// Old node updated, becomes one of two.
|
|
func d20addBranch(branch *d20branchT, node *d20nodeT, newNode **d20nodeT) bool {
|
|
if node.count < d20maxNodes { // Split won't be necessary
|
|
node.branch[node.count] = *branch
|
|
node.count++
|
|
return false
|
|
} else {
|
|
d20splitNode(node, branch, newNode)
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Disconnect a dependent node.
|
|
// Caller must return (or stop using iteration index) after this as count has changed
|
|
func d20disconnectBranch(node *d20nodeT, index int) {
|
|
// Remove element by swapping with the last element to prevent gaps in array
|
|
node.branch[index] = node.branch[node.count-1]
|
|
node.branch[node.count-1].data = nil
|
|
node.branch[node.count-1].child = nil
|
|
node.count--
|
|
}
|
|
|
|
// Pick a branch. Pick the one that will need the smallest increase
|
|
// in area to accomodate the new rectangle. This will result in the
|
|
// least total area for the covering rectangles in the current node.
|
|
// In case of a tie, pick the one which was smaller before, to get
|
|
// the best resolution when searching.
|
|
func d20pickBranch(rect *d20rectT, node *d20nodeT) int {
|
|
var firstTime bool = true
|
|
var increase float64
|
|
var bestIncr float64 = -1
|
|
var area float64
|
|
var bestArea float64
|
|
var best int
|
|
var tempRect d20rectT
|
|
|
|
for index := 0; index < node.count; index++ {
|
|
curRect := &node.branch[index].rect
|
|
area = d20calcRectVolume(curRect)
|
|
tempRect = d20combineRect(rect, curRect)
|
|
increase = d20calcRectVolume(&tempRect) - area
|
|
if (increase < bestIncr) || firstTime {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
firstTime = false
|
|
} else if (increase == bestIncr) && (area < bestArea) {
|
|
best = index
|
|
bestArea = area
|
|
bestIncr = increase
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Combine two rectangles into larger one containing both
|
|
func d20combineRect(rectA, rectB *d20rectT) d20rectT {
|
|
var newRect d20rectT
|
|
|
|
for index := 0; index < d20numDims; index++ {
|
|
newRect.min[index] = d20fmin(rectA.min[index], rectB.min[index])
|
|
newRect.max[index] = d20fmax(rectA.max[index], rectB.max[index])
|
|
}
|
|
|
|
return newRect
|
|
}
|
|
|
|
// Split a node.
|
|
// Divides the nodes branches and the extra one between two nodes.
|
|
// Old node is one of the new ones, and one really new one is created.
|
|
// Tries more than one method for choosing a partition, uses best result.
|
|
func d20splitNode(node *d20nodeT, branch *d20branchT, newNode **d20nodeT) {
|
|
// Could just use local here, but member or external is faster since it is reused
|
|
var localVars d20partitionVarsT
|
|
parVars := &localVars
|
|
|
|
// Load all the branches into a buffer, initialize old node
|
|
d20getBranches(node, branch, parVars)
|
|
|
|
// Find partition
|
|
d20choosePartition(parVars, d20minNodes)
|
|
|
|
// Create a new node to hold (about) half of the branches
|
|
*newNode = &d20nodeT{}
|
|
(*newNode).level = node.level
|
|
|
|
// Put branches from buffer into 2 nodes according to the chosen partition
|
|
node.count = 0
|
|
d20loadNodes(node, *newNode, parVars)
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
func d20rectVolume(rect *d20rectT) float64 {
|
|
var volume float64 = 1
|
|
for index := 0; index < d20numDims; index++ {
|
|
volume *= rect.max[index] - rect.min[index]
|
|
}
|
|
return volume
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given d20rectT
|
|
func d20rectSphericalVolume(rect *d20rectT) float64 {
|
|
var sumOfSquares float64 = 0
|
|
var radius float64
|
|
|
|
for index := 0; index < d20numDims; index++ {
|
|
halfExtent := (rect.max[index] - rect.min[index]) * 0.5
|
|
sumOfSquares += halfExtent * halfExtent
|
|
}
|
|
|
|
radius = math.Sqrt(sumOfSquares)
|
|
|
|
// Pow maybe slow, so test for common dims just use x*x, x*x*x.
|
|
if d20numDims == 5 {
|
|
return (radius * radius * radius * radius * radius * d20unitSphereVolume)
|
|
} else if d20numDims == 4 {
|
|
return (radius * radius * radius * radius * d20unitSphereVolume)
|
|
} else if d20numDims == 3 {
|
|
return (radius * radius * radius * d20unitSphereVolume)
|
|
} else if d20numDims == 2 {
|
|
return (radius * radius * d20unitSphereVolume)
|
|
} else {
|
|
return (math.Pow(radius, d20numDims) * d20unitSphereVolume)
|
|
}
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
func d20calcRectVolume(rect *d20rectT) float64 {
|
|
if d20useSphericalVolume {
|
|
return d20rectSphericalVolume(rect) // Slower but helps certain merge cases
|
|
} else { // RTREE_USE_SPHERICAL_VOLUME
|
|
return d20rectVolume(rect) // Faster but can cause poor merges
|
|
} // RTREE_USE_SPHERICAL_VOLUME
|
|
}
|
|
|
|
// Load branch buffer with branches from full node plus the extra branch.
|
|
func d20getBranches(node *d20nodeT, branch *d20branchT, parVars *d20partitionVarsT) {
|
|
// Load the branch buffer
|
|
for index := 0; index < d20maxNodes; index++ {
|
|
parVars.branchBuf[index] = node.branch[index]
|
|
}
|
|
parVars.branchBuf[d20maxNodes] = *branch
|
|
parVars.branchCount = d20maxNodes + 1
|
|
|
|
// Calculate rect containing all in the set
|
|
parVars.coverSplit = parVars.branchBuf[0].rect
|
|
for index := 1; index < d20maxNodes+1; index++ {
|
|
parVars.coverSplit = d20combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect)
|
|
}
|
|
parVars.coverSplitArea = d20calcRectVolume(&parVars.coverSplit)
|
|
}
|
|
|
|
// Method #0 for choosing a partition:
|
|
// As the seeds for the two groups, pick the two rects that would waste the
|
|
// most area if covered by a single rectangle, i.e. evidently the worst pair
|
|
// to have in the same group.
|
|
// Of the remaining, one at a time is chosen to be put in one of the two groups.
|
|
// The one chosen is the one with the greatest difference in area expansion
|
|
// depending on which group - the rect most strongly attracted to one group
|
|
// and repelled from the other.
|
|
// If one group gets too full (more would force other group to violate min
|
|
// fill requirement) then other group gets the rest.
|
|
// These last are the ones that can go in either group most easily.
|
|
func d20choosePartition(parVars *d20partitionVarsT, minFill int) {
|
|
var biggestDiff float64
|
|
var group, chosen, betterGroup int
|
|
|
|
d20initParVars(parVars, parVars.branchCount, minFill)
|
|
d20pickSeeds(parVars)
|
|
|
|
for ((parVars.count[0] + parVars.count[1]) < parVars.total) &&
|
|
(parVars.count[0] < (parVars.total - parVars.minFill)) &&
|
|
(parVars.count[1] < (parVars.total - parVars.minFill)) {
|
|
biggestDiff = -1
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d20notTaken == parVars.partition[index] {
|
|
curRect := &parVars.branchBuf[index].rect
|
|
rect0 := d20combineRect(curRect, &parVars.cover[0])
|
|
rect1 := d20combineRect(curRect, &parVars.cover[1])
|
|
growth0 := d20calcRectVolume(&rect0) - parVars.area[0]
|
|
growth1 := d20calcRectVolume(&rect1) - parVars.area[1]
|
|
diff := growth1 - growth0
|
|
if diff >= 0 {
|
|
group = 0
|
|
} else {
|
|
group = 1
|
|
diff = -diff
|
|
}
|
|
|
|
if diff > biggestDiff {
|
|
biggestDiff = diff
|
|
chosen = index
|
|
betterGroup = group
|
|
} else if (diff == biggestDiff) && (parVars.count[group] < parVars.count[betterGroup]) {
|
|
chosen = index
|
|
betterGroup = group
|
|
}
|
|
}
|
|
}
|
|
d20classify(chosen, betterGroup, parVars)
|
|
}
|
|
|
|
// If one group too full, put remaining rects in the other
|
|
if (parVars.count[0] + parVars.count[1]) < parVars.total {
|
|
if parVars.count[0] >= parVars.total-parVars.minFill {
|
|
group = 1
|
|
} else {
|
|
group = 0
|
|
}
|
|
for index := 0; index < parVars.total; index++ {
|
|
if d20notTaken == parVars.partition[index] {
|
|
d20classify(index, group, parVars)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy branches from the buffer into two nodes according to the partition.
|
|
func d20loadNodes(nodeA, nodeB *d20nodeT, parVars *d20partitionVarsT) {
|
|
for index := 0; index < parVars.total; index++ {
|
|
targetNodeIndex := parVars.partition[index]
|
|
targetNodes := []*d20nodeT{nodeA, nodeB}
|
|
|
|
// It is assured that d20addBranch here will not cause a node split.
|
|
d20addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil)
|
|
}
|
|
}
|
|
|
|
// Initialize a d20partitionVarsT structure.
|
|
func d20initParVars(parVars *d20partitionVarsT, maxRects, minFill int) {
|
|
parVars.count[0] = 0
|
|
parVars.count[1] = 0
|
|
parVars.area[0] = 0
|
|
parVars.area[1] = 0
|
|
parVars.total = maxRects
|
|
parVars.minFill = minFill
|
|
for index := 0; index < maxRects; index++ {
|
|
parVars.partition[index] = d20notTaken
|
|
}
|
|
}
|
|
|
|
func d20pickSeeds(parVars *d20partitionVarsT) {
|
|
var seed0, seed1 int
|
|
var worst, waste float64
|
|
var area [d20maxNodes + 1]float64
|
|
|
|
for index := 0; index < parVars.total; index++ {
|
|
area[index] = d20calcRectVolume(&parVars.branchBuf[index].rect)
|
|
}
|
|
|
|
worst = -parVars.coverSplitArea - 1
|
|
for indexA := 0; indexA < parVars.total-1; indexA++ {
|
|
for indexB := indexA + 1; indexB < parVars.total; indexB++ {
|
|
oneRect := d20combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect)
|
|
waste = d20calcRectVolume(&oneRect) - area[indexA] - area[indexB]
|
|
if waste > worst {
|
|
worst = waste
|
|
seed0 = indexA
|
|
seed1 = indexB
|
|
}
|
|
}
|
|
}
|
|
|
|
d20classify(seed0, 0, parVars)
|
|
d20classify(seed1, 1, parVars)
|
|
}
|
|
|
|
// Put a branch in one of the groups.
|
|
func d20classify(index, group int, parVars *d20partitionVarsT) {
|
|
parVars.partition[index] = group
|
|
|
|
// Calculate combined rect
|
|
if parVars.count[group] == 0 {
|
|
parVars.cover[group] = parVars.branchBuf[index].rect
|
|
} else {
|
|
parVars.cover[group] = d20combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group])
|
|
}
|
|
|
|
// Calculate volume of combined rect
|
|
parVars.area[group] = d20calcRectVolume(&parVars.cover[group])
|
|
|
|
parVars.count[group]++
|
|
}
|
|
|
|
// Delete a data rectangle from an index structure.
|
|
// Pass in a pointer to a d20rectT, the tid of the record, ptr to ptr to root node.
|
|
// Returns 1 if record not found, 0 if success.
|
|
// d20removeRect provides for eliminating the root.
|
|
func d20removeRect(rect *d20rectT, id interface{}, root **d20nodeT) bool {
|
|
var reInsertList *d20listNodeT
|
|
|
|
if !d20removeRectRec(rect, id, *root, &reInsertList) {
|
|
// Found and deleted a data item
|
|
// Reinsert any branches from eliminated nodes
|
|
for reInsertList != nil {
|
|
tempNode := reInsertList.node
|
|
|
|
for index := 0; index < tempNode.count; index++ {
|
|
// TODO go over this code. should I use (tempNode->m_level - 1)?
|
|
d20insertRect(&tempNode.branch[index], root, tempNode.level)
|
|
}
|
|
reInsertList = reInsertList.next
|
|
}
|
|
|
|
// Check for redundant root (not leaf, 1 child) and eliminate TODO replace
|
|
// if with while? In case there is a whole branch of redundant roots...
|
|
if (*root).count == 1 && (*root).isInternalNode() {
|
|
tempNode := (*root).branch[0].child
|
|
*root = tempNode
|
|
}
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Delete a rectangle from non-root part of an index structure.
|
|
// Called by d20removeRect. Descends tree recursively,
|
|
// merges branches on the way back up.
|
|
// Returns 1 if record not found, 0 if success.
|
|
func d20removeRectRec(rect *d20rectT, id interface{}, node *d20nodeT, listNode **d20listNodeT) bool {
|
|
if node.isInternalNode() { // not a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d20overlap(*rect, node.branch[index].rect) {
|
|
if !d20removeRectRec(rect, id, node.branch[index].child, listNode) {
|
|
if node.branch[index].child.count >= d20minNodes {
|
|
// child removed, just resize parent rect
|
|
node.branch[index].rect = d20nodeCover(node.branch[index].child)
|
|
} else {
|
|
// child removed, not enough entries in node, eliminate node
|
|
d20reInsert(node.branch[index].child, listNode)
|
|
d20disconnectBranch(node, index) // Must return after this call as count has changed
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else { // A leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if node.branch[index].data == id {
|
|
d20disconnectBranch(node, index) // Must return after this call as count has changed
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Decide whether two rectangles d20overlap.
|
|
func d20overlap(rectA, rectB d20rectT) bool {
|
|
for index := 0; index < d20numDims; index++ {
|
|
if rectA.min[index] > rectB.max[index] ||
|
|
rectB.min[index] > rectA.max[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Add a node to the reinsertion list. All its branches will later
|
|
// be reinserted into the index structure.
|
|
func d20reInsert(node *d20nodeT, listNode **d20listNodeT) {
|
|
newListNode := &d20listNodeT{}
|
|
newListNode.node = node
|
|
newListNode.next = *listNode
|
|
*listNode = newListNode
|
|
}
|
|
|
|
// d20search in an index tree or subtree for all data retangles that d20overlap the argument rectangle.
|
|
func d20search(node *d20nodeT, rect d20rectT, foundCount int, resultCallback func(data interface{}) bool) (int, bool) {
|
|
if node.isInternalNode() {
|
|
// This is an internal node in the tree
|
|
for index := 0; index < node.count; index++ {
|
|
if d20overlap(rect, node.branch[index].rect) {
|
|
var ok bool
|
|
foundCount, ok = d20search(node.branch[index].child, rect, foundCount, resultCallback)
|
|
if !ok {
|
|
// The callback indicated to stop searching
|
|
return foundCount, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a leaf node
|
|
for index := 0; index < node.count; index++ {
|
|
if d20overlap(rect, node.branch[index].rect) {
|
|
id := node.branch[index].data
|
|
foundCount++
|
|
if !resultCallback(id) {
|
|
return foundCount, false // Don't continue searching
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return foundCount, true // Continue searching
|
|
}
|