Updated rtree library
This commit is contained in:
parent
3ed048242e
commit
b37e7395a3
2
go.mod
2
go.mod
@ -21,7 +21,7 @@ require (
|
|||||||
github.com/tidwall/gjson v1.6.8
|
github.com/tidwall/gjson v1.6.8
|
||||||
github.com/tidwall/match v1.0.3
|
github.com/tidwall/match v1.0.3
|
||||||
github.com/tidwall/pretty v1.0.2
|
github.com/tidwall/pretty v1.0.2
|
||||||
github.com/tidwall/rbang v1.2.2
|
github.com/tidwall/rbang v1.2.3
|
||||||
github.com/tidwall/redbench v0.1.0
|
github.com/tidwall/redbench v0.1.0
|
||||||
github.com/tidwall/redcon v1.4.0
|
github.com/tidwall/redcon v1.4.0
|
||||||
github.com/tidwall/resp v0.1.0
|
github.com/tidwall/resp v0.1.0
|
||||||
|
2
go.sum
2
go.sum
@ -141,6 +141,8 @@ github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
|
|||||||
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/tidwall/rbang v1.2.2 h1:j5JZDSsybpGzCabqFpabaQNU5MCmIrlThXVUF7LD99I=
|
github.com/tidwall/rbang v1.2.2 h1:j5JZDSsybpGzCabqFpabaQNU5MCmIrlThXVUF7LD99I=
|
||||||
github.com/tidwall/rbang v1.2.2/go.mod h1:aMGOM1Wj50tooEO/0aO9j+7gyHUs3bUW0t4Q+xiuOjg=
|
github.com/tidwall/rbang v1.2.2/go.mod h1:aMGOM1Wj50tooEO/0aO9j+7gyHUs3bUW0t4Q+xiuOjg=
|
||||||
|
github.com/tidwall/rbang v1.2.3 h1:Eg48GtzQEqqwU6kxAna0H0G/m41bm/MQl/EIqU7jfK8=
|
||||||
|
github.com/tidwall/rbang v1.2.3/go.mod h1:aMGOM1Wj50tooEO/0aO9j+7gyHUs3bUW0t4Q+xiuOjg=
|
||||||
github.com/tidwall/redbench v0.1.0 h1:UZYUMhwMMObQRq5xU4SA3lmlJRztXzqtushDii+AmPo=
|
github.com/tidwall/redbench v0.1.0 h1:UZYUMhwMMObQRq5xU4SA3lmlJRztXzqtushDii+AmPo=
|
||||||
github.com/tidwall/redbench v0.1.0/go.mod h1:zxcRGCq/JcqV48YjK9WxBNJL7JSpMzbLXaHvMcnanKQ=
|
github.com/tidwall/redbench v0.1.0/go.mod h1:zxcRGCq/JcqV48YjK9WxBNJL7JSpMzbLXaHvMcnanKQ=
|
||||||
github.com/tidwall/redcon v1.4.0 h1:y2PmDD55STRdy4S98qP/Dn+gZG+cPVvIDi9BJV2aOwA=
|
github.com/tidwall/redcon v1.4.0 h1:y2PmDD55STRdy4S98qP/Dn+gZG+cPVvIDi9BJV2aOwA=
|
||||||
|
30
vendor/github.com/tidwall/rbang/README.md
generated
vendored
30
vendor/github.com/tidwall/rbang/README.md
generated
vendored
@ -1,4 +1,4 @@
|
|||||||
# `rbang`
|
# rbang
|
||||||
|
|
||||||
[](https://godoc.org/github.com/tidwall/rbang)
|
[](https://godoc.org/github.com/tidwall/rbang)
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ var tr rbang.RTree
|
|||||||
// insert a point
|
// insert a point
|
||||||
tr.Insert([2]float64{-112.0078, 33.4373}, [2]float64{-112.0078, 33.4373}, "PHX")
|
tr.Insert([2]float64{-112.0078, 33.4373}, [2]float64{-112.0078, 33.4373}, "PHX")
|
||||||
|
|
||||||
// insert a box
|
// insert a rect
|
||||||
tr.Insert([2]float64{10, 10}, [2]float64{20, 20}, "rect")
|
tr.Insert([2]float64{10, 10}, [2]float64{20, 20}, "rect")
|
||||||
|
|
||||||
// search
|
// search
|
||||||
@ -48,11 +48,11 @@ This implementation is a variant of the original paper:
|
|||||||
|
|
||||||
### Inserting
|
### Inserting
|
||||||
|
|
||||||
Same as the original algorithm. From the root to the leaf, the boxes which will incur the least enlargment are chosen. Ties go to boxes with the smallest area.
|
Same as the original algorithm. From the root to the leaf, the rects which will incur the least enlargment are chosen. Ties go to rects with the smallest area.
|
||||||
|
|
||||||
### Deleting
|
### Deleting
|
||||||
|
|
||||||
Same as the original algorithm. A target box is deleted directly. When the number of children in a box falls below it's minumum entries, it is removed from the tree and it's items are re-inserted.
|
Same as the original algorithm. A target rect is deleted directly. When the number of children in a rect falls below it's minumum entries, it is removed from the tree and it's items are re-inserted.
|
||||||
|
|
||||||
### Splitting
|
### Splitting
|
||||||
|
|
||||||
@ -60,29 +60,19 @@ This is a custom algorithm.
|
|||||||
It attempts to minimize intensive operations such as pre-sorting the children and comparing overlaps & area sizes.
|
It attempts to minimize intensive operations such as pre-sorting the children and comparing overlaps & area sizes.
|
||||||
The desire is to do simple single axis distance calculations each child only once, with a target 50/50 chance that the child might be moved in-memory.
|
The desire is to do simple single axis distance calculations each child only once, with a target 50/50 chance that the child might be moved in-memory.
|
||||||
|
|
||||||
When a box has reached it's max number of entries it's largest axis is calculated and the box is split into two smaller boxes, named `left` and `right`.
|
When a rect has reached it's max number of entries it's largest axis is calculated and the rect is split into two smaller rects, named `left` and `right`.
|
||||||
Each child boxes is then evaluated to determine which smaller box it should be placed into.
|
Each child rects is then evaluated to determine which smaller rect it should be placed into.
|
||||||
Two values, `min-dist` and `max-dist`, are calcuated for each child.
|
Two values, `min-dist` and `max-dist`, are calcuated for each child.
|
||||||
|
|
||||||
- `min-dist` is the distance from the parent's minumum value of it's largest axis to the child's minumum value of the parent largest axis.
|
- `min-dist` is the distance from the parent's minumum value of it's largest axis to the child's minumum value of the parent largest axis.
|
||||||
- `max-dist` is the distance from the parent's maximum value of it's largest axis to the child's maximum value of the parent largest axis.
|
- `max-dist` is the distance from the parent's maximum value of it's largest axis to the child's maximum value of the parent largest axis.
|
||||||
|
|
||||||
When the `min-dist` is less than `max-dist` then the child is placed into the `left` box.
|
When the `min-dist` is less than `max-dist` then the child is placed into the `left` rect.
|
||||||
When the `max-dist` is less than `min-dist` then the child is placed into the `right` box.
|
When the `max-dist` is less than `min-dist` then the child is placed into the `right` rect.
|
||||||
When the `min-dist` is equal to `max-dist` then the child is placed into an `equal` bucket until all of the children are evaluated.
|
When the `min-dist` is equal to `max-dist` then the child is placed into an `equal` bucket until all of the children are evaluated.
|
||||||
Each `equal` box is then one-by-one placed in either `left` or `right`, whichever has less children.
|
Each `equal` rect is then one-by-one placed in either `left` or `right`, whichever has less children.
|
||||||
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
In my testing:
|
|
||||||
|
|
||||||
- Insert show similar performance as the quadratic R-tree and ~1.2x - 1.5x faster than R*tree.
|
|
||||||
- Search and Delete is ~1.5x - 2x faster than quadratic and about the same as R*tree.
|
|
||||||
|
|
||||||
I hope to provide more details in the future.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
`rbang` source code is available under the MIT License.
|
rbang source code is available under the MIT License.
|
||||||
|
|
||||||
|
126
vendor/github.com/tidwall/rbang/rbang.go
generated
vendored
126
vendor/github.com/tidwall/rbang/rbang.go
generated
vendored
@ -140,18 +140,49 @@ func (tr *RTree) insert(item *rect) {
|
|||||||
tr.count++
|
tr.count++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rect) chooseLeastEnlargement(b *rect) int {
|
const inlineEnlargedArea = true
|
||||||
j, jenlargement, jarea := -1, 0.0, 0.0
|
|
||||||
|
func (r *rect) chooseLeastEnlargement(b *rect) (index int) {
|
||||||
n := r.data.(*node)
|
n := r.data.(*node)
|
||||||
|
j, jenlargement, jarea := -1, 0.0, 0.0
|
||||||
for i := 0; i < n.count; i++ {
|
for i := 0; i < n.count; i++ {
|
||||||
area := n.rects[i].area()
|
var earea float64
|
||||||
enlargement := n.rects[i].enlargedArea(b) - area
|
if inlineEnlargedArea {
|
||||||
if j == -1 || enlargement < jenlargement {
|
earea = 1.0
|
||||||
j, jenlargement, jarea = i, enlargement, area
|
if b.max[0] > n.rects[i].max[0] {
|
||||||
} else if enlargement == jenlargement {
|
if b.min[0] < n.rects[i].min[0] {
|
||||||
if area < jarea {
|
earea *= b.max[0] - b.min[0]
|
||||||
j, jenlargement, jarea = i, enlargement, area
|
} else {
|
||||||
|
earea *= b.max[0] - n.rects[i].min[0]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if b.min[0] < n.rects[i].min[0] {
|
||||||
|
earea *= n.rects[i].max[0] - b.min[0]
|
||||||
|
} else {
|
||||||
|
earea *= n.rects[i].max[0] - n.rects[i].min[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if b.max[1] > n.rects[i].max[1] {
|
||||||
|
if b.min[1] < n.rects[i].min[1] {
|
||||||
|
earea *= b.max[1] - b.min[1]
|
||||||
|
} else {
|
||||||
|
earea *= b.max[1] - n.rects[i].min[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if b.min[1] < n.rects[i].min[1] {
|
||||||
|
earea *= n.rects[i].max[1] - b.min[1]
|
||||||
|
} else {
|
||||||
|
earea *= n.rects[i].max[1] - n.rects[i].min[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
earea = n.rects[i].enlargedArea(b)
|
||||||
|
}
|
||||||
|
area := n.rects[i].area()
|
||||||
|
enlargement := earea - area
|
||||||
|
if j == -1 || enlargement < jenlargement ||
|
||||||
|
(enlargement == jenlargement && area < jarea) {
|
||||||
|
j, jenlargement, jarea = i, enlargement, area
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return j
|
return j
|
||||||
@ -233,8 +264,25 @@ func (r *rect) insert(item *rect, height int) (grown bool) {
|
|||||||
grown = !r.contains(item)
|
grown = !r.contains(item)
|
||||||
return grown
|
return grown
|
||||||
}
|
}
|
||||||
|
|
||||||
// choose subtree
|
// choose subtree
|
||||||
index := r.chooseLeastEnlargement(item)
|
index := -1
|
||||||
|
narea := 0.0
|
||||||
|
// first take a quick look for any nodes that contain the rect
|
||||||
|
for i := 0; i < n.count; i++ {
|
||||||
|
if n.rects[i].contains(item) {
|
||||||
|
area := n.rects[i].area()
|
||||||
|
if index == -1 || area < narea {
|
||||||
|
narea = area
|
||||||
|
index = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// found nothing, now go the slow path
|
||||||
|
if index == -1 {
|
||||||
|
index = r.chooseLeastEnlargement(item)
|
||||||
|
}
|
||||||
|
// insert the item into the child node
|
||||||
child := &n.rects[index]
|
child := &n.rects[index]
|
||||||
grown = child.insert(item, height-1)
|
grown = child.insert(item, height-1)
|
||||||
if grown {
|
if grown {
|
||||||
@ -374,8 +422,7 @@ func (tr *RTree) Delete(min, max [2]float64, data interface{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var removed, recalced bool
|
var removed, recalced bool
|
||||||
removed, recalced, tr.reinsert =
|
removed, recalced = tr.root.delete(tr, &item, tr.height)
|
||||||
tr.root.delete(&item, tr.height, tr.reinsert[:0])
|
|
||||||
if !removed {
|
if !removed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -393,60 +440,62 @@ func (tr *RTree) Delete(min, max [2]float64, data interface{}) {
|
|||||||
if recalced {
|
if recalced {
|
||||||
tr.root.recalc()
|
tr.root.recalc()
|
||||||
}
|
}
|
||||||
for i := range tr.reinsert {
|
if len(tr.reinsert) > 0 {
|
||||||
tr.insert(&tr.reinsert[i])
|
for i := range tr.reinsert {
|
||||||
tr.reinsert[i].data = nil
|
tr.insert(&tr.reinsert[i])
|
||||||
|
tr.reinsert[i].data = nil
|
||||||
|
}
|
||||||
|
tr.reinsert = tr.reinsert[:0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rect) delete(item *rect, height int, reinsert []rect) (
|
func (r *rect) delete(tr *RTree, item *rect, height int,
|
||||||
removed, recalced bool, reinsertOut []rect,
|
) (removed, recalced bool) {
|
||||||
) {
|
|
||||||
n := r.data.(*node)
|
n := r.data.(*node)
|
||||||
|
rects := n.rects[0:n.count]
|
||||||
if height == 0 {
|
if height == 0 {
|
||||||
for i := 0; i < n.count; i++ {
|
for i := 0; i < len(rects); i++ {
|
||||||
if n.rects[i].data == item.data {
|
if rects[i].data == item.data {
|
||||||
// found the target item to delete
|
// found the target item to delete
|
||||||
recalced = r.onEdge(&n.rects[i])
|
recalced = r.onEdge(&rects[i])
|
||||||
n.rects[i] = n.rects[n.count-1]
|
rects[i] = rects[len(rects)-1]
|
||||||
n.rects[n.count-1].data = nil
|
rects[len(rects)-1].data = nil
|
||||||
n.count--
|
n.count--
|
||||||
if recalced {
|
if recalced {
|
||||||
r.recalc()
|
r.recalc()
|
||||||
}
|
}
|
||||||
return true, recalced, reinsert
|
return true, recalced
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i := 0; i < n.count; i++ {
|
for i := 0; i < len(rects); i++ {
|
||||||
if !n.rects[i].contains(item) {
|
if !rects[i].contains(item) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
removed, recalced, reinsert =
|
removed, recalced = rects[i].delete(tr, item, height-1)
|
||||||
n.rects[i].delete(item, height-1, reinsert)
|
|
||||||
if !removed {
|
if !removed {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if n.rects[i].data.(*node).count < minEntries {
|
if rects[i].data.(*node).count < minEntries {
|
||||||
// underflow
|
// underflow
|
||||||
if !recalced {
|
if !recalced {
|
||||||
recalced = r.onEdge(&n.rects[i])
|
recalced = r.onEdge(&rects[i])
|
||||||
}
|
}
|
||||||
reinsert = n.rects[i].flatten(reinsert, height-1)
|
tr.reinsert = rects[i].flatten(tr.reinsert, height-1)
|
||||||
n.rects[i] = n.rects[n.count-1]
|
rects[i] = rects[len(rects)-1]
|
||||||
n.rects[n.count-1].data = nil
|
rects[len(rects)-1].data = nil
|
||||||
n.count--
|
n.count--
|
||||||
}
|
}
|
||||||
if recalced {
|
if recalced {
|
||||||
r.recalc()
|
r.recalc()
|
||||||
}
|
}
|
||||||
return removed, recalced, reinsert
|
return removed, recalced
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, false, reinsert
|
return false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// flatten flattens all leaf rects into a single list
|
// flatten all leaf rects into a single list
|
||||||
func (r *rect) flatten(all []rect, height int) []rect {
|
func (r *rect) flatten(all []rect, height int) []rect {
|
||||||
n := r.data.(*node)
|
n := r.data.(*node)
|
||||||
if height == 0 {
|
if height == 0 {
|
||||||
@ -525,8 +574,9 @@ func (tr *RTree) Children(
|
|||||||
return children
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace an item in the structure. This is effectively just a Delete
|
// Replace an item.
|
||||||
// followed by an Insert.
|
// This is effectively just a Delete followed by an Insert. Which means the
|
||||||
|
// new item will always be inserted, whether or not the old item was deleted.
|
||||||
func (tr *RTree) Replace(
|
func (tr *RTree) Replace(
|
||||||
oldMin, oldMax [2]float64, oldData interface{},
|
oldMin, oldMax [2]float64, oldData interface{},
|
||||||
newMin, newMax [2]float64, newData interface{},
|
newMin, newMax [2]float64, newData interface{},
|
||||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -146,7 +146,7 @@ github.com/tidwall/match
|
|||||||
# github.com/tidwall/pretty v1.0.2
|
# github.com/tidwall/pretty v1.0.2
|
||||||
## explicit
|
## explicit
|
||||||
github.com/tidwall/pretty
|
github.com/tidwall/pretty
|
||||||
# github.com/tidwall/rbang v1.2.2
|
# github.com/tidwall/rbang v1.2.3
|
||||||
## explicit
|
## explicit
|
||||||
github.com/tidwall/rbang
|
github.com/tidwall/rbang
|
||||||
# github.com/tidwall/redbench v0.1.0
|
# github.com/tidwall/redbench v0.1.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user