123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- package rtred
- import (
- "math"
- "sync"
- "github.com/tidwall/rtred/base"
- )
- type Iterator func(item Item) bool
- type Item interface {
- Rect(ctx interface{}) (min []float64, max []float64)
- }
- type RTree struct {
- dims int
- maxEntries int
- ctx interface{}
- trs []*base.RTree
- used int
- }
- func New(ctx interface{}) *RTree {
- 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")
- }
- min, max := item.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")
- }
- 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")
- }
- min, max := item.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")
- }
- 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() {
- for i := 0; i < len(tr.trs); i++ {
- tr.trs[i] = nil
- }
- tr.used = 0
- }
- func (tr *RTree) Count() int {
- 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")
- }
- 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")
- }
- 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 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]
- } else {
- amin[i] = math.Inf(-1)
- amax[i] = math.Inf(+1)
- }
- }
- var ended bool
- btr.Search(amin, amax, func(item interface{}) bool {
- if !iter(item.(Item)) {
- ended = true
- return false
- }
- return true
- })
- return !ended
- }
- 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]
- } else {
- amin[i] = math.Inf(-1)
- amax[i] = math.Inf(+1)
- }
- }
- var ended bool
- btr.KNN(amin, amax, center, func(item interface{}, dist float64) bool {
- if !iter(item.(Item), dist) {
- ended = true
- return false
- }
- return true
- })
- return !ended
- }
|