diff --git a/README.md b/README.md index fa52dc3..53a845d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Authors * 1995 Sphere volume fix for degeneracy problem submitted by Paul Brook * 2004 Templated C++ port by Greg Douglas * 2016 Go port by Josh Baker +* 2018 Added kNN and merged in some of the RBush logic by Vladimir Agafonkin License ------- diff --git a/base/knn.go b/base/knn.go new file mode 100644 index 0000000..6b26df3 --- /dev/null +++ b/base/knn.go @@ -0,0 +1,98 @@ +package base + +import ( + "github.com/tidwall/tinyqueue" +) + +type queueItem struct { + node *treeNode + isItem bool + dist float64 +} + +func (item *queueItem) Less(b tinyqueue.Item) bool { + return item.dist < b.(*queueItem).dist +} + +// KNN returns items nearest to farthest. The dist param is the "box distance". +func (tr *RTree) KNN(min, max []float64, center bool, iter func(item interface{}, dist float64) bool) bool { + var isBox bool + knnPoint := make([]float64, tr.dims) + + bbox := &treeNode{min: min, max: max} + + for i := 0; i < tr.dims; i++ { + knnPoint[i] = (bbox.min[i] + bbox.max[i]) / 2 + if !isBox && bbox.min[i] != bbox.max[i] { + isBox = true + } + } + node := tr.data + queue := tinyqueue.New(nil) + for node != nil { + for i := 0; i < node.count; i++ { + child := node.children[i] + var dist float64 + if isBox { + dist = boxDistRect(bbox, child) + } else { + dist = boxDistPoint(knnPoint, child) + } + queue.Push(&queueItem{node: child, isItem: node.leaf, dist: dist}) + } + for queue.Len() > 0 && queue.Peek().(*queueItem).isItem { + item := queue.Pop().(*queueItem) + if !iter(item.node.unsafeItem().item, item.dist) { + return false + } + } + last := queue.Pop() + if last != nil { + node = (*treeNode)(last.(*queueItem).node) + } else { + node = nil + } + } + return true +} + +func boxDistRect(a, b *treeNode) float64 { + var dist float64 + for i := 0; i < len(a.min); i++ { + var min, max float64 + if a.min[i] > b.min[i] { + min = a.min[i] + } else { + min = b.min[i] + } + if a.max[i] < b.max[i] { + max = a.max[i] + } else { + max = b.max[i] + } + squared := min - max + if squared > 0 { + dist += squared * squared + } + } + return dist +} + +func boxDistPoint(point []float64, childBox *treeNode) float64 { + var dist float64 + for i := 0; i < len(point); i++ { + d := axisDist(point[i], childBox.min[i], childBox.max[i]) + dist += d * d + } + return dist +} + +func axisDist(k, min, max float64) float64 { + if k < min { + return min - k + } + if k <= max { + return 0 + } + return k - max +} diff --git a/base/load.go b/base/load.go new file mode 100644 index 0000000..bf6954f --- /dev/null +++ b/base/load.go @@ -0,0 +1,97 @@ +package base + +import "math" + +// Load bulk load items into the R-tree. +func (tr *RTree) Load(mins, maxs [][]float64, items []interface{}) { + if len(items) < tr.minEntries { + for i := 0; i < len(items); i++ { + tr.Insert(mins[i], maxs[i], items[i]) + } + return + } + + // prefill the items + fitems := make([]*treeNode, len(items)) + for i := 0; i < len(items); i++ { + item := &treeItem{min: mins[i], max: maxs[i], item: items[i]} + fitems[i] = item.unsafeNode() + } + + // following equations are defined in the paper describing OMT + N := len(fitems) + M := tr.maxEntries + h := int(math.Ceil(math.Log(float64(N)) / math.Log(float64(M)))) + Nsubtree := int(math.Pow(float64(M), float64(h-1))) + S := int(math.Ceil(math.Sqrt(float64(N) / float64(Nsubtree)))) + + // sort by the initial axis + axis := 0 + sortByAxis(fitems, axis) + + // build the root node. it's split differently from the subtrees. + children := make([]*treeNode, 0, S) + for i := 0; i < S; i++ { + var part []*treeNode + if i == S-1 { + // last split + part = fitems[len(fitems)/S*i:] + } else { + part = fitems[len(fitems)/S*i : len(fitems)/S*(i+1)] + } + children = append(children, tr.omt(part, h-1, axis+1)) + } + + node := tr.createNode(children) + node.leaf = false + node.height = h + tr.calcBBox(node) + + if tr.data.count == 0 { + // save as is if tree is empty + tr.data = node + } else if tr.data.height == node.height { + // split root if trees have the same height + tr.splitRoot(tr.data, node) + } else { + if tr.data.height < node.height { + // swap trees if inserted one is bigger + tr.data, node = node, tr.data + } + + // insert the small tree into the large tree at appropriate level + tr.insert(node, nil, tr.data.height-node.height-1, true) + } +} + +func (tr *RTree) omt(fitems []*treeNode, h, axis int) *treeNode { + if len(fitems) <= tr.maxEntries { + // reached leaf level; return leaf + children := make([]*treeNode, len(fitems)) + copy(children, fitems) + node := tr.createNode(children) + node.height = h + tr.calcBBox(node) + return node + } + + // sort the items on a different axis than the previous level. + sortByAxis(fitems, axis%tr.dims) + children := make([]*treeNode, 0, tr.maxEntries) + partsz := len(fitems) / tr.maxEntries + for i := 0; i < tr.maxEntries; i++ { + var part []*treeNode + if i == tr.maxEntries-1 { + // last part + part = fitems[partsz*i:] + } else { + part = fitems[partsz*i : partsz*(i+1)] + } + children = append(children, tr.omt(part, h-1, axis+1)) + } + node := tr.createNode(children) + node.height = h + node.leaf = false + tr.calcBBox(node) + return node +} diff --git a/base/rtree.go b/base/rtree.go new file mode 100644 index 0000000..4a66235 --- /dev/null +++ b/base/rtree.go @@ -0,0 +1,673 @@ +package base + +import ( + "math" + "unsafe" +) + +// precalculate infinity +var mathInfNeg = math.Inf(-1) +var mathInfPos = math.Inf(+1) + +type treeNode struct { + min, max []float64 + children []*treeNode + count int + height int + leaf bool +} + +func (node *treeNode) unsafeItem() *treeItem { + return (*treeItem)(unsafe.Pointer(node)) +} + +func (tr *RTree) createNode(children []*treeNode) *treeNode { + n := &treeNode{ + height: 1, + leaf: true, + children: make([]*treeNode, tr.maxEntries+1), + } + if len(children) > 0 { + n.count = len(children) + copy(n.children[:n.count], children) + } + n.min = make([]float64, tr.dims) + n.max = make([]float64, tr.dims) + for i := 0; i < tr.dims; i++ { + n.min[i] = mathInfPos + n.max[i] = mathInfNeg + } + return n +} + +func (node *treeNode) extend(b *treeNode) { + for i := 0; i < len(node.min); i++ { + if b.min[i] < node.min[i] { + node.min[i] = b.min[i] + } + if b.max[i] > node.max[i] { + node.max[i] = b.max[i] + } + } +} + +func (node *treeNode) area() float64 { + area := node.max[0] - node.min[0] + for i := 1; i < len(node.min); i++ { + area *= node.max[i] - node.min[i] + } + return area +} + +func (node *treeNode) enlargedAreaAxis(b *treeNode, axis int) float64 { + var max, min float64 + if b.max[axis] > node.max[axis] { + max = b.max[axis] + } else { + max = node.max[axis] + } + if b.min[axis] < node.min[axis] { + min = b.min[axis] + } else { + min = node.min[axis] + } + return max - min +} + +func (node *treeNode) enlargedArea(b *treeNode) float64 { + area := node.enlargedAreaAxis(b, 0) + for i := 1; i < len(node.min); i++ { + area *= node.enlargedAreaAxis(b, i) + } + return area +} + +func (node *treeNode) intersectionAreaAxis(b *treeNode, axis int) float64 { + var max, min float64 + if node.max[axis] < b.max[axis] { + max = node.max[axis] + } else { + max = b.max[axis] + } + if node.min[axis] > b.min[axis] { + min = node.min[axis] + } else { + min = b.min[axis] + } + if max > min { + return max - min + } + return 0 +} +func (node *treeNode) intersectionArea(b *treeNode) float64 { + area := node.intersectionAreaAxis(b, 0) + for i := 1; i < len(node.min); i++ { + area *= node.intersectionAreaAxis(b, i) + } + return area +} +func (node *treeNode) margin() float64 { + margin := node.max[0] - node.min[0] + for i := 1; i < len(node.min); i++ { + margin += node.max[i] - node.min[i] + } + return margin +} + +type result int + +const ( + not result = 0 + intersects result = 1 + contains result = 2 +) + +func (node *treeNode) overlaps(b *treeNode) result { + for i := 0; i < len(node.min); i++ { + if b.min[i] > node.max[i] || b.max[i] < node.min[i] { + return not + } + if node.min[i] > b.min[i] || b.max[i] > node.max[i] { + i++ + for ; i < len(node.min); i++ { + if b.min[i] > node.max[i] || b.max[i] < node.min[i] { + return not + } + } + return intersects + } + } + return contains +} + +func (node *treeNode) intersects(b *treeNode) bool { + for i := 0; i < len(node.min); i++ { + if b.min[i] > node.max[i] || b.max[i] < node.min[i] { + return false + } + } + return true +} + +func (node *treeNode) findItem(item interface{}) int { + for i := 0; i < node.count; i++ { + if node.children[i].unsafeItem().item == item { + return i + } + } + return -1 +} + +func (node *treeNode) contains(b *treeNode) bool { + for i := 0; i < len(node.min); i++ { + if node.min[i] > b.min[i] || b.max[i] > node.max[i] { + return false + } + } + return true +} + +func (node *treeNode) childCount() int { + if node.leaf { + return node.count + } + var n int + for i := 0; i < node.count; i++ { + n += node.children[i].childCount() + } + return n +} + +type treeItem struct { + min, max []float64 + item interface{} +} + +func (item *treeItem) unsafeNode() *treeNode { + return (*treeNode)(unsafe.Pointer(item)) +} + +// RTree is an R-tree +type RTree struct { + dims int + maxEntries int + minEntries int + data *treeNode // root node + // resusable fields, these help performance of common mutable operations. + reuse struct { + path []*treeNode // for reinsertion path + indexes []int // for remove function + stack []int // for bulk loading + } +} + +// New creates a new R-tree +func New(dims, maxEntries int) *RTree { + if dims <= 0 { + panic("invalid dimensions") + } + + tr := &RTree{} + tr.dims = dims + tr.maxEntries = int(math.Max(4, float64(maxEntries))) + tr.minEntries = int(math.Max(2, math.Ceil(float64(tr.maxEntries)*0.4))) + tr.data = tr.createNode(nil) + return tr +} + +// Insert inserts an item +func (tr *RTree) Insert(min, max []float64, item interface{}) { + if len(min) != tr.dims || len(max) != tr.dims { + panic("invalid dimensions") + } + if item == nil { + panic("nil item") + } + bbox := treeNode{min: min, max: max} + tr.insert(&bbox, item, tr.data.height-1, false) +} + +func (tr *RTree) insert(bbox *treeNode, item interface{}, level int, isNode bool) { + tr.reuse.path = tr.reuse.path[:0] + node, insertPath := tr.chooseSubtree(bbox, tr.data, level, tr.reuse.path) + if item == nil { + // item is only nil when bulk loading a node + if node.leaf { + panic("loading node into leaf") + } + node.children[node.count] = bbox + node.count++ + } else { + ti := &treeItem{min: bbox.min, max: bbox.max, item: item} + node.children[node.count] = ti.unsafeNode() + node.count++ + } + node.extend(bbox) + for level >= 0 { + if insertPath[level].count > tr.maxEntries { + insertPath = tr.split(insertPath, level) + level-- + } else { + break + } + } + tr.adjustParentBBoxes(bbox, insertPath, level) + tr.reuse.path = insertPath +} + +func (tr *RTree) adjustParentBBoxes(bbox *treeNode, path []*treeNode, level int) { + // adjust bboxes along the given tree path + for i := level; i >= 0; i-- { + path[i].extend(bbox) + } +} + +func (tr *RTree) chooseSubtree(bbox, node *treeNode, level int, path []*treeNode) (*treeNode, []*treeNode) { + var targetNode *treeNode + var area, enlargement, minArea, minEnlargement float64 + for { + path = append(path, node) + if node.leaf || len(path)-1 == level { + break + } + minEnlargement = mathInfPos + minArea = minEnlargement + for i := 0; i < node.count; i++ { + child := node.children[i] + area = child.area() + enlargement = bbox.enlargedArea(child) - area + if enlargement < minEnlargement { + minEnlargement = enlargement + if area < minArea { + minArea = area + } + targetNode = child + } else if enlargement == minEnlargement { + if area < minArea { + minArea = area + targetNode = child + } + } + } + if targetNode != nil { + node = targetNode + } else if node.count > 0 { + node = (*treeNode)(node.children[0]) + } else { + node = nil + } + } + return node, path +} +func (tr *RTree) split(insertPath []*treeNode, level int) []*treeNode { + var node = insertPath[level] + var M = node.count + var m = tr.minEntries + + tr.chooseSplitAxis(node, m, M) + splitIndex := tr.chooseSplitIndex(node, m, M) + + spliced := make([]*treeNode, node.count-splitIndex) + copy(spliced, node.children[splitIndex:]) + node.count = splitIndex + + newNode := tr.createNode(spliced) + newNode.height = node.height + newNode.leaf = node.leaf + + tr.calcBBox(node) + tr.calcBBox(newNode) + + if level != 0 { + insertPath[level-1].children[insertPath[level-1].count] = newNode + insertPath[level-1].count++ + } else { + tr.splitRoot(node, newNode) + } + return insertPath +} +func (tr *RTree) chooseSplitIndex(node *treeNode, m, M int) int { + var i int + var bbox1, bbox2 *treeNode + var overlap, area, minOverlap, minArea float64 + var index int + + minArea = mathInfPos + minOverlap = minArea + + for i = m; i <= M-m; i++ { + bbox1 = tr.distBBox(node, 0, i, nil) + bbox2 = tr.distBBox(node, i, M, nil) + + overlap = bbox1.intersectionArea(bbox2) + area = bbox1.area() + bbox2.area() + + // choose distribution with minimum overlap + if overlap < minOverlap { + minOverlap = overlap + index = i + + if area < minArea { + minArea = area + } + } else if overlap == minOverlap { + // otherwise choose distribution with minimum area + if area < minArea { + minArea = area + index = i + } + } + } + return index +} +func (tr *RTree) calcBBox(node *treeNode) { + tr.distBBox(node, 0, node.count, node) +} +func (tr *RTree) chooseSplitAxis(node *treeNode, m, M int) { + minMargin := tr.allDistMargin(node, m, M, 0) + var minAxis int + for axis := 1; axis < tr.dims; axis++ { + margin := tr.allDistMargin(node, m, M, axis) + if margin < minMargin { + minMargin = margin + minAxis = axis + } + } + if minAxis < tr.dims { + tr.sortNodes(node, minAxis) + } +} +func (tr *RTree) splitRoot(node, newNode *treeNode) { + tr.data = tr.createNode([]*treeNode{node, newNode}) + tr.data.height = node.height + 1 + tr.data.leaf = false + tr.calcBBox(tr.data) +} +func (tr *RTree) distBBox(node *treeNode, k, p int, destNode *treeNode) *treeNode { + if destNode == nil { + destNode = tr.createNode(nil) + } else { + for i := 0; i < tr.dims; i++ { + destNode.min[i] = mathInfPos + destNode.max[i] = mathInfNeg + } + } + for i := k; i < p; i++ { + if node.leaf { + destNode.extend(node.children[i]) + } else { + destNode.extend((*treeNode)(node.children[i])) + } + } + return destNode +} +func (tr *RTree) allDistMargin(node *treeNode, m, M int, axis int) float64 { + tr.sortNodes(node, axis) + + var leftBBox = tr.distBBox(node, 0, m, nil) + var rightBBox = tr.distBBox(node, M-m, M, nil) + var margin = leftBBox.margin() + rightBBox.margin() + + var i int + + if node.leaf { + for i = m; i < M-m; i++ { + leftBBox.extend(node.children[i]) + margin += leftBBox.margin() + } + for i = M - m - 1; i >= m; i-- { + leftBBox.extend(node.children[i]) + margin += rightBBox.margin() + } + } else { + for i = m; i < M-m; i++ { + child := (*treeNode)(node.children[i]) + leftBBox.extend(child) + margin += leftBBox.margin() + } + for i = M - m - 1; i >= m; i-- { + child := (*treeNode)(node.children[i]) + leftBBox.extend(child) + margin += rightBBox.margin() + } + } + return margin +} +func (tr *RTree) sortNodes(node *treeNode, axis int) { + sortByAxis(node.children[:node.count], axis) +} + +func sortByAxis(items []*treeNode, axis int) { + if len(items) < 2 { + return + } + left, right := 0, len(items)-1 + pivotIndex := len(items) / 2 + items[pivotIndex], items[right] = items[right], items[pivotIndex] + for i := range items { + if items[i].min[axis] < items[right].min[axis] { + items[i], items[left] = items[left], items[i] + left++ + } + } + items[left], items[right] = items[right], items[left] + sortByAxis(items[:left], axis) + sortByAxis(items[left+1:], axis) +} + +// Search searches the tree for items in the input rectangle +func (tr *RTree) Search(min, max []float64, iter func(item interface{}) bool) bool { + bbox := &treeNode{min: min, max: max} + if !tr.data.intersects(bbox) { + return true + } + return tr.search(tr.data, bbox, iter) +} + +func (tr *RTree) search(node, bbox *treeNode, iter func(item interface{}) bool) bool { + if node.leaf { + for i := 0; i < node.count; i++ { + if bbox.intersects(node.children[i]) { + if !iter(node.children[i].unsafeItem().item) { + return false + } + } + } + } else { + for i := 0; i < node.count; i++ { + r := bbox.overlaps(node.children[i]) + if r == intersects { + if !tr.search(node.children[i], bbox, iter) { + return false + } + } else if r == contains { + if !scan(node.children[i], iter) { + return false + } + } + } + } + return true +} + +func (tr *RTree) IsEmpty() bool { + empty := true + tr.Scan(func(item interface{}) bool { + empty = false + return false + }) + return empty +} + +// Remove removes an item from the R-tree. +func (tr *RTree) Remove(min, max []float64, item interface{}) { + bbox := &treeNode{min: min, max: max} + tr.remove(bbox, item) +} + +func (tr *RTree) remove(bbox *treeNode, item interface{}) { + path := tr.reuse.path[:0] + indexes := tr.reuse.indexes[:0] + + var node = tr.data + var i int + var parent *treeNode + var index int + var goingUp bool + + for node != nil || len(path) != 0 { + if node == nil { + node = path[len(path)-1] + path = path[:len(path)-1] + if len(path) == 0 { + parent = nil + } else { + parent = path[len(path)-1] + } + i = indexes[len(indexes)-1] + indexes = indexes[:len(indexes)-1] + goingUp = true + } + + if node.leaf { + index = node.findItem(item) + if index != -1 { + // item found, remove the item and condense tree upwards + copy(node.children[index:], node.children[index+1:]) + node.children[node.count-1] = nil + node.count-- + path = append(path, node) + tr.condense(path) + goto done + } + } + if !goingUp && !node.leaf && node.contains(bbox) { // go down + path = append(path, node) + indexes = append(indexes, i) + i = 0 + parent = node + node = (*treeNode)(node.children[0]) + } else if parent != nil { // go right + i++ + if i == parent.count { + node = nil + } else { + node = (*treeNode)(parent.children[i]) + } + goingUp = false + } else { + node = nil + } + } +done: + tr.reuse.path = path + tr.reuse.indexes = indexes + return +} +func (tr *RTree) condense(path []*treeNode) { + // go through the path, removing empty nodes and updating bboxes + var siblings []*treeNode + for i := len(path) - 1; i >= 0; i-- { + if path[i].count == 0 { + if i > 0 { + siblings = path[i-1].children[:path[i-1].count] + index := -1 + for j := 0; j < len(siblings); j++ { + if siblings[j] == path[i] { + index = j + break + } + } + copy(siblings[index:], siblings[index+1:]) + siblings[len(siblings)-1] = nil + path[i-1].count-- + //siblings = siblings[:len(siblings)-1] + //path[i-1].children = siblings + } else { + tr.data = tr.createNode(nil) // clear tree + } + } else { + tr.calcBBox(path[i]) + } + } +} + +// Count returns the number of items in the R-tree. +func (tr *RTree) Count() int { + return tr.data.childCount() +} + +// Traverse iterates over the entire R-tree and includes all nodes and items. +func (tr *RTree) Traverse(iter func(min, max []float64, level int, item interface{}) bool) bool { + return tr.traverse(tr.data, iter) +} + +func (tr *RTree) traverse(node *treeNode, iter func(min, max []float64, level int, item interface{}) bool) bool { + if !iter(node.min, node.max, int(node.height), nil) { + return false + } + if node.leaf { + for i := 0; i < node.count; i++ { + child := node.children[i] + if !iter(child.min, child.max, 0, child.unsafeItem().item) { + return false + } + } + } else { + for i := 0; i < node.count; i++ { + child := node.children[i] + if !tr.traverse(child, iter) { + return false + } + } + } + return true +} + +// Scan iterates over the entire R-tree +func (tr *RTree) Scan(iter func(item interface{}) bool) bool { + return scan(tr.data, iter) +} + +func scan(node *treeNode, iter func(item interface{}) bool) bool { + if node.leaf { + for i := 0; i < node.count; i++ { + child := node.children[i] + if !iter(child.unsafeItem().item) { + return false + } + } + } else { + for i := 0; i < node.count; i++ { + child := node.children[i] + if !scan(child, iter) { + return false + } + } + } + return true +} + +// Bounds returns the bounding box of the entire R-tree +func (tr *RTree) Bounds() (min, max []float64) { + if tr.data.count > 0 { + return tr.data.min, tr.data.max + } + return make([]float64, tr.dims), make([]float64, tr.dims) +} + +// Complexity returns the complexity of the R-tree. The higher the value, the +// more complex the tree. The value of 1 is the lowest. +func (tr *RTree) Complexity() float64 { + var nodeCount int + var itemCount int + tr.Traverse(func(_, _ []float64, level int, _ interface{}) bool { + if level == 0 { + itemCount++ + } else { + nodeCount++ + } + return true + }) + return float64(tr.maxEntries*nodeCount) / float64(itemCount) +} diff --git a/base/rtree_test.go b/base/rtree_test.go new file mode 100644 index 0000000..f4bc154 --- /dev/null +++ b/base/rtree_test.go @@ -0,0 +1,584 @@ +package base + +import ( + "fmt" + "log" + "math" + "math/rand" + "reflect" + "runtime" + "sort" + "testing" + "time" +) + +const D = 2 +const M = 13 + +type Rect struct { + min, max []float64 + item interface{} +} + +func (r *Rect) equals(r2 Rect) bool { + if len(r.min) != len(r2.min) || len(r.max) != len(r2.max) || r.item != r2.item { + return false + } + for i := 0; i < len(r.min); i++ { + if r.min[i] != r2.min[i] { + return false + } + } + for i := 0; i < len(r.max); i++ { + if r.max[i] != r2.max[i] { + return false + } + } + return true +} + +func ptrMakePoint(vals ...float64) *Rect { + var r Rect + r.min = make([]float64, D) + r.max = make([]float64, D) + for i := 0; i < D && i < len(vals); i++ { + r.min[i] = vals[i] + r.max[i] = vals[i] + } + r.item = &r + return &r +} + +func ptrMakeRect(vals ...float64) *Rect { + var r Rect + r.min = make([]float64, D) + r.max = make([]float64, D) + for i := 0; i < D && i < len(vals); i++ { + r.min[i] = vals[i] + r.max[i] = vals[i+D] + } + r.item = &r + return &r +} + +func TestRTree(t *testing.T) { + tr := New(D, M) + p := ptrMakePoint(10, 10) + tr.Insert(p.min, p.max, p.item) +} + +func TestPtrBasic2D(t *testing.T) { + if D != 2 { + return + } + tr := New(D, M) + p1 := ptrMakePoint(-115, 33) + p2 := ptrMakePoint(-113, 35) + tr.Insert(p1.min, p1.max, p1.item) + tr.Insert(p2.min, p2.max, p2.item) + assertEqual(t, 2, tr.Count()) + + var points []*Rect + bbox := ptrMakeRect(-116, 32, -114, 34) + tr.Search(bbox.min, bbox.max, func(item interface{}) bool { + points = append(points, item.(*Rect)) + return true + }) + assertEqual(t, 1, len(points)) + tr.Remove(p1.min, p1.max, p1.item) + assertEqual(t, 1, tr.Count()) + + points = nil + bbox = ptrMakeRect(-116, 33, -114, 34) + tr.Search(bbox.min, bbox.max, func(item interface{}) bool { + points = append(points, item.(*Rect)) + return true + }) + assertEqual(t, 0, len(points)) + tr.Remove(p2.min, p2.max, p2.item) + assertEqual(t, 0, tr.Count()) +} + +func getMemStats() runtime.MemStats { + runtime.GC() + time.Sleep(time.Millisecond) + runtime.GC() + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + return ms +} + +func ptrMakeRandom(what string) *Rect { + if what == "point" { + vals := make([]float64, D) + for i := 0; i < D; i++ { + if i == 0 { + vals[i] = rand.Float64()*360 - 180 + } else if i == 1 { + vals[i] = rand.Float64()*180 - 90 + } else { + vals[i] = rand.Float64()*100 - 50 + } + } + return ptrMakePoint(vals...) + } else if what == "rect" { + vals := make([]float64, D) + for i := 0; i < D; i++ { + if i == 0 { + vals[i] = rand.Float64()*340 - 170 + } else if i == 1 { + vals[i] = rand.Float64()*160 - 80 + } else { + vals[i] = rand.Float64()*80 - 30 + } + } + rvals := make([]float64, D*2) + for i := 0; i < D; i++ { + rvals[i] = vals[i] - rand.Float64()*10 + rvals[D+i] = vals[i] + rand.Float64()*10 + } + return ptrMakeRect(rvals...) + } + panic("??") +} + +func TestPtrRandom(t *testing.T) { + t.Run(fmt.Sprintf("%dD", D), func(t *testing.T) { + t.Run("point", func(t *testing.T) { ptrTestRandom(t, "point", 10000) }) + t.Run("rect", func(t *testing.T) { ptrTestRandom(t, "rect", 10000) }) + }) +} + +func ptrTestRandom(t *testing.T, which string, n int) { + fmt.Println("-------------------------------------------------") + fmt.Printf("Testing Random %dD %ss\n", D, which) + fmt.Println("-------------------------------------------------") + rand.Seed(time.Now().UnixNano()) + tr := New(D, M) + min, max := tr.Bounds() + assertEqual(t, make([]float64, D), min[:]) + assertEqual(t, make([]float64, D), max[:]) + + // create random objects + m1 := getMemStats() + objs := make([]*Rect, n) + for i := 0; i < n; i++ { + objs[i] = ptrMakeRandom(which) + } + + // insert the objects into tree + m2 := getMemStats() + start := time.Now() + for _, r := range objs { + tr.Insert(r.min, r.max, r.item) + } + durInsert := time.Since(start) + m3 := getMemStats() + assertEqual(t, len(objs), tr.Count()) + fmt.Printf("Inserted %d random %ss in %dms -- %d ops/sec\n", + len(objs), which, int(durInsert.Seconds()*1000), + int(float64(len(objs))/durInsert.Seconds())) + fmt.Printf(" total cost is %d bytes/%s\n", int(m3.HeapAlloc-m1.HeapAlloc)/len(objs), which) + fmt.Printf(" tree cost is %d bytes/%s\n", int(m3.HeapAlloc-m2.HeapAlloc)/len(objs), which) + fmt.Printf(" tree overhead %d%%\n", int((float64(m3.HeapAlloc-m2.HeapAlloc)/float64(len(objs)))/(float64(m3.HeapAlloc-m1.HeapAlloc)/float64(len(objs)))*100)) + fmt.Printf(" complexity %f\n", tr.Complexity()) + + start = time.Now() + // count all nodes and leaves + var nodes int + var leaves int + var maxLevel int + tr.Traverse(func(min, max []float64, level int, item interface{}) bool { + if level != 0 { + nodes++ + } + if level == 1 { + leaves++ + } + if level > maxLevel { + maxLevel = level + } + return true + }) + fmt.Printf(" nodes: %d, leaves: %d, level: %d\n", nodes, leaves, maxLevel) + + // verify mbr + for i := 0; i < D; i++ { + min[i] = math.Inf(+1) + max[i] = math.Inf(-1) + } + for _, o := range objs { + for i := 0; i < D; i++ { + if o.min[i] < min[i] { + min[i] = o.min[i] + } + if o.max[i] > max[i] { + max[i] = o.max[i] + } + } + } + minb, maxb := tr.Bounds() + assertEqual(t, min, minb) + assertEqual(t, max, maxb) + + // scan + var arr []*Rect + tr.Scan(func(item interface{}) bool { + arr = append(arr, item.(*Rect)) + return true + }) + assertEqual(t, true, ptrTestHasSameItems(objs, arr)) + + // search + ptrTestSearch(t, tr, objs, 0.10, true) + ptrTestSearch(t, tr, objs, 0.50, true) + ptrTestSearch(t, tr, objs, 1.00, true) + + // knn + ptrTestKNN(t, tr, objs, int(float64(len(objs))*0.01), true) + ptrTestKNN(t, tr, objs, int(float64(len(objs))*0.50), true) + ptrTestKNN(t, tr, objs, int(float64(len(objs))*1.00), true) + + // remove all objects + indexes := rand.Perm(len(objs)) + start = time.Now() + for _, i := range indexes { + tr.Remove(objs[i].min, objs[i].max, objs[i].item) + } + durRemove := time.Since(start) + assertEqual(t, 0, tr.Count()) + fmt.Printf("Removed %d random %ss in %dms -- %d ops/sec\n", + len(objs), which, int(durRemove.Seconds()*1000), + int(float64(len(objs))/durRemove.Seconds())) + + min, max = tr.Bounds() + assertEqual(t, make([]float64, D), min[:]) + assertEqual(t, make([]float64, D), max[:]) +} + +func ptrTestHasSameItems(a1, a2 []*Rect) bool { + if len(a1) != len(a2) { + return false + } + for _, p1 := range a1 { + var found bool + for _, p2 := range a2 { + if p1.equals(*p2) { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +func ptrTestSearch(t *testing.T, tr *RTree, objs []*Rect, percent float64, check bool) { + var found int + var start time.Time + var stop time.Time + defer func() { + dur := stop.Sub(start) + fmt.Printf("Searched %.0f%% (%d/%d items) in %dms -- %d ops/sec\n", + percent*100, found, len(objs), int(dur.Seconds()*1000), + int(float64(1)/dur.Seconds()), + ) + }() + min, max := tr.Bounds() + vals := make([]float64, D*2) + for i := 0; i < D; i++ { + vals[i] = ((max[i]+min[i])/2 - ((max[i]-min[i])*percent)/2) + vals[D+i] = ((max[i]+min[i])/2 + ((max[i]-min[i])*percent)/2) + } + var arr1 []*Rect + var box *Rect + if percent == 1 { + box = ptrMakeRect(append(append([]float64{}, min[:]...), max[:]...)...) + } else { + box = ptrMakeRect(vals...) + } + start = time.Now() + tr.Search(box.min, box.max, func(item interface{}) bool { + if check { + arr1 = append(arr1, item.(*Rect)) + } + found++ + return true + }) + stop = time.Now() + if !check { + return + } + var arr2 []*Rect + for _, obj := range objs { + if ptrTestIntersects(obj, box) { + arr2 = append(arr2, obj) + } + } + assertEqual(t, len(arr1), len(arr2)) + for _, o1 := range arr1 { + var found bool + for _, o2 := range arr2 { + if o2.equals(*o1) { + found = true + break + } + } + if !found { + t.Fatalf("not found") + } + } +} + +func ptrTestKNN(t *testing.T, tr *RTree, objs []*Rect, n int, check bool) { + var start time.Time + var stop time.Time + defer func() { + dur := stop.Sub(start) + fmt.Printf("KNN %d items in %dms -- %d ops/sec\n", + n, int(dur.Seconds()*1000), + int(float64(1)/dur.Seconds()), + ) + }() + min, max := tr.Bounds() + pvals := make([]float64, D) + for i := 0; i < D; i++ { + pvals[i] = (max[i] + min[i]) / 2 + } + point := ptrMakePoint(pvals...) + + // gather the results, make sure that is matches exactly + var arr1 []Rect + var dists1 []float64 + pdist := math.Inf(-1) + start = time.Now() + tr.KNN(point.min, point.max, false, func(item interface{}, dist float64) bool { + if len(arr1) == n { + return false + } + arr1 = append(arr1, Rect{min: min, max: max, item: item}) + dists1 = append(dists1, dist) + if dist < pdist { + panic("dist out of order") + } + pdist = dist + return true + }) + stop = time.Now() + assertEqual(t, true, n > len(objs) || n == len(arr1)) + + // get the KNN for the original array + nobjs := make([]*Rect, len(objs)) + copy(nobjs, objs) + sort.Slice(nobjs, func(i, j int) bool { + idist := ptrTestBoxDist(pvals, nobjs[i].min, nobjs[i].max) + jdist := ptrTestBoxDist(pvals, nobjs[j].min, nobjs[j].max) + return idist < jdist + }) + arr2 := nobjs[:len(arr1)] + var dists2 []float64 + for i := 0; i < len(arr2); i++ { + dist := ptrTestBoxDist(pvals, arr2[i].min, arr2[i].max) + dists2 = append(dists2, dist) + } + // only compare the distances, not the objects because rectangles with + // a dist of zero will not be ordered. + assertEqual(t, dists1, dists2) + +} + +func ptrTestBoxDist(point []float64, min, max []float64) float64 { + var dist float64 + for i := 0; i < len(point); i++ { + d := ptrTestAxisDist(point[i], min[i], max[i]) + dist += d * d + } + return dist +} +func ptrTestAxisDist(k, min, max float64) float64 { + if k < min { + return min - k + } + if k <= max { + return 0 + } + return k - max +} +func ptrTestIntersects(obj, box *Rect) bool { + for i := 0; i < D; i++ { + if box.min[i] > obj.max[i] || box.max[i] < obj.min[i] { + return false + } + } + return true +} + +// func TestPtrInsertFlatPNG2D(t *testing.T) { +// fmt.Println("-------------------------------------------------") +// fmt.Println("Generating Cities PNG 2D (flat-insert-2d.png)") +// fmt.Println("-------------------------------------------------") +// tr := New() +// var items []*Rect +// c := cities.Cities +// for i := 0; i < len(c); i++ { +// x := c[i].Longitude +// y := c[i].Latitude +// items = append(items, ptrMakePoint(x, y)) +// } +// start := time.Now() +// for _, item := range items { +// tr.Insert(item.min, item.max, item.item) +// } +// dur := time.Since(start) +// fmt.Printf("wrote %d cities (flat) in %s (%.0f/ops)\n", len(c), dur, float64(len(c))/dur.Seconds()) +// withGIF := os.Getenv("GIFOUTPUT") != "" +// if err := tr.SavePNG("ptr-flat-insert-2d.png", 1000, 1000, 1.25/360.0, 0, true, withGIF, os.Stdout); err != nil { +// t.Fatal(err) +// } +// if !withGIF { +// fmt.Println("use GIFOUTPUT=1 for animated gif") +// } +// } + +// func TestPtrLoadFlatPNG2D(t *testing.T) { +// fmt.Println("-------------------------------------------------") +// fmt.Println("Generating Cities 2D PNG (flat-load-2d.png)") +// fmt.Println("-------------------------------------------------") +// tr := New() +// var items []*Rect +// c := cities.Cities +// for i := 0; i < len(c); i++ { +// x := c[i].Longitude +// y := c[i].Latitude +// items = append(items, ptrMakePoint(x, y)) +// } + +// var mins [][D]float64 +// var maxs [][D]float64 +// var ifs []interface{} +// for i := 0; i < len(items); i++ { +// mins = append(mins, items[i].min) +// maxs = append(maxs, items[i].max) +// ifs = append(ifs, items[i].item) +// } + +// start := time.Now() +// tr.Load(mins, maxs, ifs) +// dur := time.Since(start) + +// if true { +// var all []*Rect +// tr.Scan(func(min, max [D]float64, item interface{}) bool { +// all = append(all, &Rect{min: min, max: max, item: item}) +// return true +// }) +// assertEqual(t, len(all), len(items)) + +// for len(all) > 0 { +// item := all[0] +// var found bool +// for _, city := range items { +// if *city == *item { +// found = true +// break +// } +// } +// if !found { +// t.Fatal("item not found") +// } +// all = all[1:] +// } +// } +// fmt.Printf("wrote %d cities (flat) in %s (%.0f/ops)\n", len(c), dur, float64(len(c))/dur.Seconds()) +// withGIF := os.Getenv("GIFOUTPUT") != "" +// if err := tr.SavePNG("ptr-flat-load-2d.png", 1000, 1000, 1.25/360.0, 0, true, withGIF, os.Stdout); err != nil { +// t.Fatal(err) +// } +// if !withGIF { +// fmt.Println("use GIFOUTPUT=1 for animated gif") +// } +// } + +func TestBenchmarks(t *testing.T) { + var points []*Rect + for i := 0; i < 2000000; i++ { + x := rand.Float64()*360 - 180 + y := rand.Float64()*180 - 90 + points = append(points, ptrMakePoint(x, y)) + } + tr := New(D, M) + start := time.Now() + for i := len(points) / 2; i < len(points); i++ { + tr.Insert(points[i].min, points[i].max, points[i].item) + } + dur := time.Since(start) + log.Printf("insert 1M items one by one: %.3fs", dur.Seconds()) + //// + rarr := rand.Perm(len(points) / 2) + start = time.Now() + for i := 0; i < len(points)/2; i++ { + a := points[rarr[i]+len(points)/2] + b := points[rarr[i]] + tr.Remove(a.min, a.max, a.item) + tr.Insert(b.min, b.max, b.item) + } + dur = time.Since(start) + log.Printf("replaced 1M items one by one: %.3fs", dur.Seconds()) + points = points[:len(points)/2] + //// + start = time.Now() + for i := 0; i < 1000; i++ { + tr.Remove(points[i].min, points[i].max, points[i].item) + } + dur = time.Since(start) + log.Printf("remove 100 items one by one: %.3fs", dur.Seconds()) + //// + bbox := ptrMakeRect(0, 0, 0+(360*0.0001), 0+(180*0.0001)) + start = time.Now() + for i := 0; i < 1000; i++ { + tr.Search(bbox.min, bbox.max, func(_ interface{}) bool { return true }) + } + dur = time.Since(start) + log.Printf("1000 searches of 0.01%% area: %.3fs", dur.Seconds()) + //// + bbox = ptrMakeRect(0, 0, 0+(360*0.01), 0+(180*0.01)) + start = time.Now() + for i := 0; i < 1000; i++ { + tr.Search(bbox.min, bbox.max, func(_ interface{}) bool { return true }) + } + dur = time.Since(start) + log.Printf("1000 searches of 1%% area: %.3fs", dur.Seconds()) + //// + bbox = ptrMakeRect(0, 0, 0+(360*0.10), 0+(180*0.10)) + start = time.Now() + for i := 0; i < 1000; i++ { + tr.Search(bbox.min, bbox.max, func(_ interface{}) bool { return true }) + } + dur = time.Since(start) + log.Printf("1000 searches of 10%% area: %.3fs", dur.Seconds()) + /// + + var mins [][]float64 + var maxs [][]float64 + var items []interface{} + for i := 0; i < len(points); i++ { + mins = append(mins, points[i].min) + maxs = append(maxs, points[i].max) + items = append(items, points[i].item) + } + + tr = New(D, M) + start = time.Now() + tr.Load(mins, maxs, items) + dur = time.Since(start) + log.Printf("bulk-insert 1M items: %.3fs", dur.Seconds()) +} + +func assertEqual(t *testing.T, expected, actual interface{}) { + t.Helper() + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected '%v', got '%v'", expected, actual) + } +} diff --git a/gen/gen.go b/gen/gen.go deleted file mode 100644 index 2a70992..0000000 --- a/gen/gen.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "flag" - "io/ioutil" - "log" - "os" - "strconv" - "strings" -) - -func main() { - var dims int - var debug bool - flag.IntVar(&dims, "dims", 4, "number of dimensions") - flag.BoolVar(&debug, "debug", false, "turn on debug tracing") - flag.Parse() - // process rtree.go - data, err := ioutil.ReadFile("src/rtree.go") - if err != nil { - log.Fatal(err) - } - data = []byte(strings.Replace(string(data), "// +build ignore", "// generated; DO NOT EDIT!", -1)) - if debug { - data = []byte(strings.Replace(string(data), "TDEBUG", "true", -1)) - } else { - data = []byte(strings.Replace(string(data), "TDEBUG", "false", -1)) - } - var dimouts = make([]string, dims) - var output string - var recording bool - lines := strings.Split(string(data), "\n") - for _, line := range lines { - if strings.HasPrefix(strings.TrimSpace(line), "//") { - idx := strings.Index(line, "//") - switch strings.ToUpper(strings.TrimSpace(line[idx+2:])) { - case "BEGIN": - recording = true - for i := 0; i < len(dimouts); i++ { - dimouts[i] = "" - } - continue - case "END": - for _, out := range dimouts { - if out != "" { - output += out - } - } - recording = false - continue - } - } - if recording { - for i := 0; i < len(dimouts); i++ { - dimouts[i] += strings.Replace(line, "TNUMDIMS", strconv.FormatInt(int64(i+1), 10), -1) + "\n" - } - } else { - output += line + "\n" - } - } - // process rtree_base.go - if err := os.RemoveAll("../dims"); err != nil { - log.Fatal(err) - } - for i := 0; i < dims; i++ { - sdim := strconv.FormatInt(int64(i+1), 10) - data, err := ioutil.ReadFile("src/rtree_base.go") - if err != nil { - log.Fatal(err) - } - data = []byte(strings.Split(string(data), "// FILE_START")[1]) - if debug { - data = []byte(strings.Replace(string(data), "TDEBUG", "true", -1)) - } else { - data = []byte(strings.Replace(string(data), "TDEBUG", "false", -1)) - } - data = []byte(strings.Replace(string(data), "TNUMDIMS", strconv.FormatInt(int64(i+1), 10), -1)) - data = []byte(strings.Replace(string(data), "DD_", "d"+strconv.FormatInt(int64(i+1), 10), -1)) - if err := os.MkdirAll("../dims/d"+sdim, 0777); err != nil { - log.Fatal(err) - } - output = string(append([]byte(output), data...)) - } - if err := ioutil.WriteFile("../rtree.go", []byte(output), 0666); err != nil { - log.Fatal(err) - } -} diff --git a/gen/gen.sh b/gen/gen.sh deleted file mode 100755 index edf3189..0000000 --- a/gen/gen.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -set -e - -cd $(dirname "${BASH_SOURCE[0]}") - -go run gen.go --dims=20 --debug=false -cd .. -go fmt diff --git a/gen/src/rtree.go b/gen/src/rtree.go deleted file mode 100644 index 5c1a438..0000000 --- a/gen/src/rtree.go +++ /dev/null @@ -1,134 +0,0 @@ -// +build ignore - -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{} - // BEGIN - trTNUMDIMS *dTNUMDIMSRTree - // END -} - -func New(ctx interface{}) *RTree { - return &RTree{ - ctx: ctx, - // BEGIN - trTNUMDIMS: dTNUMDIMSNew(), - // END - } -} - -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") - // BEGIN - case TNUMDIMS: - var amin, amax [TNUMDIMS]float64 - for i := 0; i < len(min); i++ { - amin[i], amax[i] = min[i], max[i] - } - tr.trTNUMDIMS.Insert(amin, amax, item) - // END - } -} - -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") - // BEGIN - case TNUMDIMS: - var amin, amax [TNUMDIMS]float64 - for i := 0; i < len(min); i++ { - amin[i], amax[i] = min[i], max[i] - } - tr.trTNUMDIMS.Remove(amin, amax, item) - // END - } -} -func (tr *RTree) Reset() { - // BEGIN - tr.trTNUMDIMS = dTNUMDIMSNew() - // END -} -func (tr *RTree) Count() int { - count := 0 - // BEGIN - count += tr.trTNUMDIMS.Count() - // END - 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") - // BEGIN - case TNUMDIMS: - // END - } - // BEGIN - if !tr.searchTNUMDIMS(min, max, iter) { - return - } - // END -} - -// BEGIN -func (tr *RTree) searchTNUMDIMS(min, max []float64, iter Iterator) bool { - var amin, amax [TNUMDIMS]float64 - for i := 0; i < TNUMDIMS; 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.trTNUMDIMS.Search(amin, amax, func(dataID interface{}) bool { - if !iter(dataID.(Item)) { - ended = true - return false - } - return true - }) - return !ended -} - -// END diff --git a/gen/src/rtree_base.go b/gen/src/rtree_base.go deleted file mode 100644 index 81d2163..0000000 --- a/gen/src/rtree_base.go +++ /dev/null @@ -1,687 +0,0 @@ -// +build ignore - -/* - -TITLE - - R-TREES: A DYNAMIC INDEX STRUCTURE FOR SPATIAL SEARCHING - -DESCRIPTION - - A Go version of the RTree algorithm. - -AUTHORS - - * 1983 Original algorithm and test code by Antonin Guttman and Michael Stonebraker, UC Berkely - * 1994 ANCI C ported from original test code by Melinda Green - melinda@superliminal.com - * 1995 Sphere volume fix for degeneracy problem submitted by Paul Brook - * 2004 Templated C++ port by Greg Douglas - * 2016 Go port by Josh Baker - -LICENSE: - - Entirely free for all uses. Enjoy! - -*/ - -// Implementation of RTree, a multidimensional bounding rectangle tree. -package rtree - -import "math" - -// FILE_START - -func DD_fmin(a, b float64) float64 { - if a < b { - return a - } - return b -} -func DD_fmax(a, b float64) float64 { - if a > b { - return a - } - return b -} - -const ( - DD_numDims = TNUMDIMS - DD_maxNodes = 8 - DD_minNodes = DD_maxNodes / 2 - DD_useSphericalVolume = true // Better split classification, may be slower on some systems -) - -var DD_unitSphereVolume = []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 -}[DD_numDims] - -type DD_RTree struct { - root *DD_nodeT ///< Root of tree -} - -/// Minimal bounding rectangle (n-dimensional) -type DD_rectT struct { - min [DD_numDims]float64 ///< Min dimensions of bounding box - max [DD_numDims]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 DD_branchT struct { - rect DD_rectT ///< Bounds - child *DD_nodeT ///< Child node - data interface{} ///< Data Id or Ptr -} - -/// DD_nodeT for each branch level -type DD_nodeT struct { - count int ///< Count - level int ///< Leaf is zero, others positive - branch [DD_maxNodes]DD_branchT ///< Branch -} - -func (node *DD_nodeT) isInternalNode() bool { - return (node.level > 0) // Not a leaf, but a internal node -} -func (node *DD_nodeT) isLeaf() bool { - return (node.level == 0) // A leaf, contains data -} - -/// A link list of nodes for reinsertion after a delete operation -type DD_listNodeT struct { - next *DD_listNodeT ///< Next in list - node *DD_nodeT ///< Node -} - -const DD_notTaken = -1 // indicates that position - -/// Variables for finding a split partition -type DD_partitionVarsT struct { - partition [DD_maxNodes + 1]int - total int - minFill int - count [2]int - cover [2]DD_rectT - area [2]float64 - - branchBuf [DD_maxNodes + 1]DD_branchT - branchCount int - coverSplit DD_rectT - coverSplitArea float64 -} - -func DD_New() *DD_RTree { - // 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 &DD_RTree{ - root: &DD_nodeT{}, - } -} - -/// 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 *DD_RTree) Insert(min, max [DD_numDims]float64, dataId interface{}) { - var branch DD_branchT - branch.data = dataId - for axis := 0; axis < DD_numDims; axis++ { - branch.rect.min[axis] = min[axis] - branch.rect.max[axis] = max[axis] - } - DD_insertRect(&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 *DD_RTree) Remove(min, max [DD_numDims]float64, dataId interface{}) { - var rect DD_rectT - for axis := 0; axis < DD_numDims; axis++ { - rect.min[axis] = min[axis] - rect.max[axis] = max[axis] - } - DD_removeRect(&rect, dataId, &tr.root) -} - -/// Find all within DD_search rectangle -/// \param a_min Min of DD_search bounding rect -/// \param a_max Max of DD_search bounding rect -/// \param a_searchResult DD_search 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 *DD_RTree) Search(min, max [DD_numDims]float64, resultCallback func(data interface{}) bool) int { - var rect DD_rectT - for axis := 0; axis < DD_numDims; axis++ { - rect.min[axis] = min[axis] - rect.max[axis] = max[axis] - } - foundCount, _ := DD_search(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 *DD_RTree) Count() int { - var count int - DD_countRec(tr.root, &count) - return count -} - -/// Remove all entries from tree -func (tr *DD_RTree) RemoveAll() { - // Delete all existing nodes - tr.root = &DD_nodeT{} -} - -func DD_countRec(node *DD_nodeT, count *int) { - if node.isInternalNode() { // not a leaf node - for index := 0; index < node.count; index++ { - DD_countRec(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 DD_insertRectRec(branch *DD_branchT, node *DD_nodeT, newNode **DD_nodeT, 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 *DD_nodeT - //var newBranch DD_branchT - - // find the optimal branch for this record - index := DD_pickBranch(&branch.rect, node) - - // recursively insert this record into the picked branch - childWasSplit := DD_insertRectRec(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 = DD_combineRect(&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 = DD_nodeCover(node.branch[index].child) - var newBranch DD_branchT - newBranch.child = otherNode - newBranch.rect = DD_nodeCover(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 DD_addBranch(&newBranch, node, newNode) - } - } else if node.level == level { - // We have reached level for insertion. Add rect, split if necessary - return DD_addBranch(branch, node, newNode) - } else { - // Should never occur - return false - } -} - -// Insert a data rectangle into an index structure. -// DD_insertRect 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 DD_insertRect(branch *DD_branchT, root **DD_nodeT, level int) bool { - var newNode *DD_nodeT - - if DD_insertRectRec(branch, *root, &newNode, level) { // Root split - - // Grow tree taller and new root - newRoot := &DD_nodeT{} - newRoot.level = (*root).level + 1 - - var newBranch DD_branchT - - // add old root node as a child of the new root - newBranch.rect = DD_nodeCover(*root) - newBranch.child = *root - DD_addBranch(&newBranch, newRoot, nil) - - // add the split node as a child of the new root - newBranch.rect = DD_nodeCover(newNode) - newBranch.child = newNode - DD_addBranch(&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 DD_nodeCover(node *DD_nodeT) DD_rectT { - rect := node.branch[0].rect - for index := 1; index < node.count; index++ { - rect = DD_combineRect(&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 DD_addBranch(branch *DD_branchT, node *DD_nodeT, newNode **DD_nodeT) bool { - if node.count < DD_maxNodes { // Split won't be necessary - node.branch[node.count] = *branch - node.count++ - return false - } else { - DD_splitNode(node, branch, newNode) - return true - } -} - -// Disconnect a dependent node. -// Caller must return (or stop using iteration index) after this as count has changed -func DD_disconnectBranch(node *DD_nodeT, 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 DD_pickBranch(rect *DD_rectT, node *DD_nodeT) int { - var firstTime bool = true - var increase float64 - var bestIncr float64 = -1 - var area float64 - var bestArea float64 - var best int - var tempRect DD_rectT - - for index := 0; index < node.count; index++ { - curRect := &node.branch[index].rect - area = DD_calcRectVolume(curRect) - tempRect = DD_combineRect(rect, curRect) - increase = DD_calcRectVolume(&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 DD_combineRect(rectA, rectB *DD_rectT) DD_rectT { - var newRect DD_rectT - - for index := 0; index < DD_numDims; index++ { - newRect.min[index] = DD_fmin(rectA.min[index], rectB.min[index]) - newRect.max[index] = DD_fmax(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 DD_splitNode(node *DD_nodeT, branch *DD_branchT, newNode **DD_nodeT) { - // Could just use local here, but member or external is faster since it is reused - var localVars DD_partitionVarsT - parVars := &localVars - - // Load all the branches into a buffer, initialize old node - DD_getBranches(node, branch, parVars) - - // Find partition - DD_choosePartition(parVars, DD_minNodes) - - // Create a new node to hold (about) half of the branches - *newNode = &DD_nodeT{} - (*newNode).level = node.level - - // Put branches from buffer into 2 nodes according to the chosen partition - node.count = 0 - DD_loadNodes(node, *newNode, parVars) -} - -// Calculate the n-dimensional volume of a rectangle -func DD_rectVolume(rect *DD_rectT) float64 { - var volume float64 = 1 - for index := 0; index < DD_numDims; index++ { - volume *= rect.max[index] - rect.min[index] - } - return volume -} - -// The exact volume of the bounding sphere for the given DD_rectT -func DD_rectSphericalVolume(rect *DD_rectT) float64 { - var sumOfSquares float64 = 0 - var radius float64 - - for index := 0; index < DD_numDims; 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 DD_numDims == 5 { - return (radius * radius * radius * radius * radius * DD_unitSphereVolume) - } else if DD_numDims == 4 { - return (radius * radius * radius * radius * DD_unitSphereVolume) - } else if DD_numDims == 3 { - return (radius * radius * radius * DD_unitSphereVolume) - } else if DD_numDims == 2 { - return (radius * radius * DD_unitSphereVolume) - } else { - return (math.Pow(radius, DD_numDims) * DD_unitSphereVolume) - } -} - -// Use one of the methods to calculate retangle volume -func DD_calcRectVolume(rect *DD_rectT) float64 { - if DD_useSphericalVolume { - return DD_rectSphericalVolume(rect) // Slower but helps certain merge cases - } else { // RTREE_USE_SPHERICAL_VOLUME - return DD_rectVolume(rect) // Faster but can cause poor merges - } // RTREE_USE_SPHERICAL_VOLUME -} - -// Load branch buffer with branches from full node plus the extra branch. -func DD_getBranches(node *DD_nodeT, branch *DD_branchT, parVars *DD_partitionVarsT) { - // Load the branch buffer - for index := 0; index < DD_maxNodes; index++ { - parVars.branchBuf[index] = node.branch[index] - } - parVars.branchBuf[DD_maxNodes] = *branch - parVars.branchCount = DD_maxNodes + 1 - - // Calculate rect containing all in the set - parVars.coverSplit = parVars.branchBuf[0].rect - for index := 1; index < DD_maxNodes+1; index++ { - parVars.coverSplit = DD_combineRect(&parVars.coverSplit, &parVars.branchBuf[index].rect) - } - parVars.coverSplitArea = DD_calcRectVolume(&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 DD_choosePartition(parVars *DD_partitionVarsT, minFill int) { - var biggestDiff float64 - var group, chosen, betterGroup int - - DD_initParVars(parVars, parVars.branchCount, minFill) - DD_pickSeeds(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 DD_notTaken == parVars.partition[index] { - curRect := &parVars.branchBuf[index].rect - rect0 := DD_combineRect(curRect, &parVars.cover[0]) - rect1 := DD_combineRect(curRect, &parVars.cover[1]) - growth0 := DD_calcRectVolume(&rect0) - parVars.area[0] - growth1 := DD_calcRectVolume(&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 - } - } - } - DD_classify(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 DD_notTaken == parVars.partition[index] { - DD_classify(index, group, parVars) - } - } - } -} - -// Copy branches from the buffer into two nodes according to the partition. -func DD_loadNodes(nodeA, nodeB *DD_nodeT, parVars *DD_partitionVarsT) { - for index := 0; index < parVars.total; index++ { - targetNodeIndex := parVars.partition[index] - targetNodes := []*DD_nodeT{nodeA, nodeB} - - // It is assured that DD_addBranch here will not cause a node split. - DD_addBranch(&parVars.branchBuf[index], targetNodes[targetNodeIndex], nil) - } -} - -// Initialize a DD_partitionVarsT structure. -func DD_initParVars(parVars *DD_partitionVarsT, 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] = DD_notTaken - } -} - -func DD_pickSeeds(parVars *DD_partitionVarsT) { - var seed0, seed1 int - var worst, waste float64 - var area [DD_maxNodes + 1]float64 - - for index := 0; index < parVars.total; index++ { - area[index] = DD_calcRectVolume(&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 := DD_combineRect(&parVars.branchBuf[indexA].rect, &parVars.branchBuf[indexB].rect) - waste = DD_calcRectVolume(&oneRect) - area[indexA] - area[indexB] - if waste > worst { - worst = waste - seed0 = indexA - seed1 = indexB - } - } - } - - DD_classify(seed0, 0, parVars) - DD_classify(seed1, 1, parVars) -} - -// Put a branch in one of the groups. -func DD_classify(index, group int, parVars *DD_partitionVarsT) { - parVars.partition[index] = group - - // Calculate combined rect - if parVars.count[group] == 0 { - parVars.cover[group] = parVars.branchBuf[index].rect - } else { - parVars.cover[group] = DD_combineRect(&parVars.branchBuf[index].rect, &parVars.cover[group]) - } - - // Calculate volume of combined rect - parVars.area[group] = DD_calcRectVolume(&parVars.cover[group]) - - parVars.count[group]++ -} - -// Delete a data rectangle from an index structure. -// Pass in a pointer to a DD_rectT, the tid of the record, ptr to ptr to root node. -// Returns 1 if record not found, 0 if success. -// DD_removeRect provides for eliminating the root. -func DD_removeRect(rect *DD_rectT, id interface{}, root **DD_nodeT) bool { - var reInsertList *DD_listNodeT - - if !DD_removeRectRec(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)? - DD_insertRect(&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 DD_removeRect. Descends tree recursively, -// merges branches on the way back up. -// Returns 1 if record not found, 0 if success. -func DD_removeRectRec(rect *DD_rectT, id interface{}, node *DD_nodeT, listNode **DD_listNodeT) bool { - if node.isInternalNode() { // not a leaf node - for index := 0; index < node.count; index++ { - if DD_overlap(*rect, node.branch[index].rect) { - if !DD_removeRectRec(rect, id, node.branch[index].child, listNode) { - if node.branch[index].child.count >= DD_minNodes { - // child removed, just resize parent rect - node.branch[index].rect = DD_nodeCover(node.branch[index].child) - } else { - // child removed, not enough entries in node, eliminate node - DD_reInsert(node.branch[index].child, listNode) - DD_disconnectBranch(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 { - DD_disconnectBranch(node, index) // Must return after this call as count has changed - return false - } - } - return true - } -} - -// Decide whether two rectangles DD_overlap. -func DD_overlap(rectA, rectB DD_rectT) bool { - for index := 0; index < DD_numDims; 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 DD_reInsert(node *DD_nodeT, listNode **DD_listNodeT) { - newListNode := &DD_listNodeT{} - newListNode.node = node - newListNode.next = *listNode - *listNode = newListNode -} - -// DD_search in an index tree or subtree for all data retangles that DD_overlap the argument rectangle. -func DD_search(node *DD_nodeT, rect DD_rectT, 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 DD_overlap(rect, node.branch[index].rect) { - var ok bool - foundCount, ok = DD_search(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 DD_overlap(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 -} diff --git a/rtree.go b/rtree.go index 330a1f5..bbbb1ff 100644 --- a/rtree.go +++ b/rtree.go @@ -1,8 +1,11 @@ -// generated; DO NOT EDIT! - package rtree -import "math" +import ( + "math" + "sync" + + "github.com/tidwall/rtree/base" +) type Iterator func(item Item) bool type Item interface { @@ -10,372 +13,94 @@ type Item interface { } 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 + dims int + maxEntries int + ctx interface{} + trs []*base.RTree + used int } 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(), + tr := &RTree{ + ctx: ctx, + dims: 20, + maxEntries: 13, } + tr.trs = make([]*base.RTree, 20) + return tr } func (tr *RTree) Insert(item Item) { if item == nil { - panic("nil item being added to RTree") + panic("nil item") } min, max := item.Rect(tr.ctx) if len(min) != len(max) { return // just return panic("invalid item rectangle") } - switch len(min) { - default: + if len(min) < 1 || len(min) > len(tr.trs) { 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) } + btr := tr.trs[len(min)-1] + if btr == nil { + btr = base.New(len(min), tr.maxEntries) + tr.trs[len(min)-1] = btr + tr.used++ + } + amin := make([]float64, len(min)) + amax := make([]float64, len(max)) + for i := 0; i < len(min); i++ { + amin[i], amax[i] = min[i], max[i] + } + btr.Insert(amin, amax, item) } func (tr *RTree) Remove(item Item) { if item == nil { - panic("nil item being added to RTree") + panic("nil item") } min, max := item.Rect(tr.ctx) if len(min) != len(max) { return // just return panic("invalid item rectangle") } - switch len(min) { - default: + if len(min) < 1 || len(min) > len(tr.trs) { 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) + } + btr := tr.trs[len(min)-1] + if btr == nil { + return + } + amin := make([]float64, len(min)) + amax := make([]float64, len(max)) + for i := 0; i < len(min); i++ { + amin[i], amax[i] = min[i], max[i] + } + btr.Remove(amin, amax, item) + if btr.IsEmpty() { + tr.trs[len(min)-1] = nil + tr.used-- } } 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() + for i := 0; i < len(tr.trs); i++ { + tr.trs[i] = nil + } + tr.used = 0 } 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() + var count int + for _, btr := range tr.trs { + if btr != nil { + count += btr.Count() + } + } return count } + func (tr *RTree) Search(bounds Item, iter Iterator) { if bounds == nil { panic("nil bounds being used for search") @@ -385,96 +110,27 @@ func (tr *RTree) Search(bounds Item, iter Iterator) { return // just return panic("invalid item rectangle") } - switch len(min) { - default: + if len(min) < 1 || len(min) > len(tr.trs) { 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 + used := tr.used + for i, btr := range tr.trs { + if used == 0 { + break + } + if btr != nil { + if !search(btr, min, max, i+1, iter) { + return + } + used-- + } } } - -func (tr *RTree) search1(min, max []float64, iter Iterator) bool { - var amin, amax [1]float64 - for i := 0; i < 1; i++ { +func search(btr *base.RTree, min, max []float64, dims int, iter Iterator) bool { + amin := make([]float64, dims) + amax := make([]float64, dims) + for i := 0; i < dims; i++ { if i < len(min) { amin[i] = min[i] amax[i] = max[i] @@ -483,9 +139,9 @@ func (tr *RTree) search1(min, max []float64, iter Iterator) bool { amax[i] = math.Inf(+1) } } - ended := false - tr.tr1.Search(amin, amax, func(dataID interface{}) bool { - if !iter(dataID.(Item)) { + var ended bool + btr.Search(amin, amax, func(item interface{}) bool { + if !iter(item.(Item)) { ended = true return false } @@ -494,9 +150,114 @@ func (tr *RTree) search1(min, max []float64, iter Iterator) bool { return !ended } -func (tr *RTree) search2(min, max []float64, iter Iterator) bool { - var amin, amax [2]float64 - for i := 0; i < 2; i++ { +func (tr *RTree) KNN(bounds Item, center bool, iter func(item Item, dist float64) bool) { + 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") + } + if len(min) < 1 || len(min) > len(tr.trs) { + return // just return + panic("invalid dimension") + } + + if tr.used == 0 { + return + } + if tr.used == 1 { + for i, btr := range tr.trs { + if btr != nil { + knn(btr, min, max, center, i+1, func(item interface{}, dist float64) bool { + return iter(item.(Item), dist) + }) + break + } + } + return + } + + type queueT struct { + done bool + step int + item Item + dist float64 + } + + var mu sync.Mutex + var ended bool + queues := make(map[int][]queueT) + cond := sync.NewCond(&mu) + for i, btr := range tr.trs { + if btr != nil { + dims := i + 1 + mu.Lock() + queues[dims] = []queueT{} + cond.Signal() + mu.Unlock() + go func(dims int, btr *base.RTree) { + knn(btr, min, max, center, dims, func(item interface{}, dist float64) bool { + mu.Lock() + if ended { + mu.Unlock() + return false + } + queues[dims] = append(queues[dims], queueT{item: item.(Item), dist: dist}) + cond.Signal() + mu.Unlock() + return true + }) + mu.Lock() + queues[dims] = append(queues[dims], queueT{done: true}) + cond.Signal() + mu.Unlock() + }(dims, btr) + } + } + mu.Lock() + for { + ready := true + for i := range queues { + if len(queues[i]) == 0 { + ready = false + break + } + if queues[i][0].done { + delete(queues, i) + } + } + if len(queues) == 0 { + break + } + if ready { + var j int + var minDist float64 + var minItem Item + var minQueue int + for i := range queues { + if j == 0 || queues[i][0].dist < minDist { + minDist = queues[i][0].dist + minItem = queues[i][0].item + minQueue = i + } + } + queues[minQueue] = queues[minQueue][1:] + if !iter(minItem, minDist) { + ended = true + break + } + continue + } + cond.Wait() + } + mu.Unlock() +} +func knn(btr *base.RTree, min, max []float64, center bool, dims int, iter func(item interface{}, dist float64) bool) bool { + amin := make([]float64, dims) + amax := make([]float64, dims) + for i := 0; i < dims; i++ { if i < len(min) { amin[i] = min[i] amax[i] = max[i] @@ -505,9 +266,9 @@ func (tr *RTree) search2(min, max []float64, iter Iterator) bool { amax[i] = math.Inf(+1) } } - ended := false - tr.tr2.Search(amin, amax, func(dataID interface{}) bool { - if !iter(dataID.(Item)) { + var ended bool + btr.KNN(amin, amax, center, func(item interface{}, dist float64) bool { + if !iter(item.(Item), dist) { ended = true return false } @@ -515,13499 +276,3 @@ func (tr *RTree) search2(min, max []float64, iter Iterator) bool { }) 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 -} diff --git a/rtree_test.go b/rtree_test.go index cd79e7a..4574a16 100644 --- a/rtree_test.go +++ b/rtree_test.go @@ -167,3 +167,59 @@ func BenchmarkInsert(t *testing.B) { t.StartTimer() } + +func TestKNN(t *testing.T) { + n := 25000 + tr := New(nil) + var points []*tPoint + rand.Seed(1) + for i := 0; i < n; i++ { + r := tRandPoint() + points = append(points, r) + tr.Insert(r) + } + if tr.Count() != n { + t.Fatalf("expecting %v, got %v", n, tr.Count()) + } + var count int + tr.Search(&tRect{-100, -100, -100, -100, 100, 100, 100, 100}, func(item Item) bool { + count++ + return true + }) + var pdist float64 + var i int + center := []float64{50, 50} + centerRect := &tRect{center[0], center[1], center[0], center[1]} + tr.KNN(centerRect, true, func(item Item, dist float64) bool { + dist2 := boxDistPoint(center, item) + if i > 0 && dist2 < pdist { + t.Fatal("out of order") + } + pdist = dist + i++ + return true + }) + if i != n { + t.Fatal("mismatch") + } +} + +func boxDistPoint(point []float64, item Item) float64 { + var dist float64 + min, max := item.Rect(nil) + for i := 0; i < len(point); i++ { + d := axisDist(point[i], min[i], max[i]) + dist += d * d + } + return dist +} + +func axisDist(k, min, max float64) float64 { + if k < min { + return min - k + } + if k <= max { + return 0 + } + return k - max +}