├── .gitignore ├── cities.png ├── go.mod ├── .github └── workflows │ └── go.yml ├── go.sum ├── LICENSE ├── README.md └── rtree.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.svg 2 | *.prof -------------------------------------------------------------------------------- /cities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidwall/rtree/HEAD/cities.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tidwall/rtree 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/tidwall/geoindex v1.7.0 7 | github.com/tidwall/lotsa v1.0.2 8 | ) 9 | 10 | require github.com/tidwall/cities v0.1.0 // indirect 11 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.19 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/tidwall/cities v0.1.0 h1:CVNkmMf7NEC9Bvokf5GoSsArHCKRMTgLuubRTHnH0mE= 2 | github.com/tidwall/cities v0.1.0/go.mod h1:lV/HDp2gCcRcHJWqgt6Di54GiDrTZwh1aG2ZUPNbqa4= 3 | github.com/tidwall/geoindex v1.7.0 h1:jtk41sfgwIt8MEDyC3xyKSj75iXXf6rjReJGDNPtR5o= 4 | github.com/tidwall/geoindex v1.7.0/go.mod h1:rvVVNEFfkJVWGUdEfU8QaoOg/9zFX0h9ofWzA60mz1I= 5 | github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= 6 | github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Josh Baker 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rtree 2 | 3 | [![GoDoc](https://godoc.org/github.com/tidwall/rtree?status.svg)](https://godoc.org/github.com/tidwall/rtree) 4 | 5 | This package provides an in-memory R-Tree implementation for Go. It's designed 6 | for [Tile38](https://github.com/tidwall/tile38) and is optimized for fast rect 7 | inserts and replacements. 8 | 9 | Cities 10 | 11 | ## Usage 12 | 13 | ### Installing 14 | 15 | To start using rtree, install Go and run `go get`: 16 | 17 | ```sh 18 | $ go get -u github.com/tidwall/rtree 19 | ``` 20 | 21 | ### Basic operations 22 | 23 | ```go 24 | // create a 2D RTree 25 | var tr rtree.RTree 26 | 27 | // insert a point 28 | tr.Insert([2]float64{-112.0078, 33.4373}, [2]float64{-112.0078, 33.4373}, "PHX") 29 | 30 | // insert a rect 31 | tr.Insert([2]float64{10, 10}, [2]float64{20, 20}, "rect") 32 | 33 | // search 34 | tr.Search([2]float64{-112.1, 33.4}, [2]float64{-112.0, 33.5}, 35 | func(min, max [2]float64, data interface{}) bool { 36 | println(data.(string)) // prints "PHX" 37 | }, 38 | ) 39 | 40 | // delete 41 | tr.Delete([2]float64{-112.0078, 33.4373}, [2]float64{-112.0078, 33.4373}, "PHX") 42 | ``` 43 | 44 | ### Support for Generics (Go 1.18+) 45 | 46 | ```go 47 | // create a 2D RTree 48 | var tr rtree.RTreeG[string] 49 | 50 | // insert a point 51 | tr.Insert([2]float64{-112.0078, 33.4373}, [2]float64{-112.0078, 33.4373}, "PHX") 52 | 53 | // insert a rect 54 | tr.Insert([2]float64{10, 10}, [2]float64{20, 20}, "rect") 55 | 56 | // search 57 | tr.Search([2]float64{-112.1, 33.4}, [2]float64{-112.0, 33.5}, 58 | func(min, max [2]float64, data string) bool { 59 | println(data) // prints "PHX" 60 | }, 61 | ) 62 | 63 | // delete 64 | tr.Delete([2]float64{-112.0078, 33.4373}, [2]float64{-112.0078, 33.4373}, "PHX") 65 | ``` 66 | 67 | 68 | ### Support for generic numeric types, like int, float32, etc. 69 | 70 | ```go 71 | // create a 2D RTree 72 | var tr rtree.RTreeGN[float32, string] 73 | 74 | // insert a point 75 | tr.Insert([2]float32{-112.0078, 33.4373}, [2]float32{-112.0078, 33.4373}, "PHX") 76 | 77 | // insert a rect 78 | tr.Insert([2]float32{10, 10}, [2]float32{20, 20}, "rect") 79 | 80 | // search 81 | tr.Search([2]float32{-112.1, 33.4}, [2]float32{-112.0, 33.5}, 82 | func(min, max [2]float32, data string) bool { 83 | println(data) // prints "PHX" 84 | }, 85 | ) 86 | 87 | // delete 88 | tr.Delete([2]float32{-112.0078, 33.4373}, [2]float32{-112.0078, 33.4373}, "PHX") 89 | ``` 90 | 91 | 92 | ## Algorithms 93 | 94 | This implementation is a variant of the original paper: 95 | [R-TREES. A DYNAMIC INDEX STRUCTURE FOR SPATIAL SEARCHING](https://www.cs.princeton.edu/courses/archive/fall08/cos597B/papers/rtrees.pdf) 96 | 97 | ### Inserting 98 | 99 | Similar to the original paper. From the root to the leaf, the rects which will incur the least enlargment are chosen. Ties go to rects with the smallest area. 100 | 101 | Added to this implementation: when a rect does not incur any enlargement at all, it's chosen immediately and without further checks on other rects in the same node. Also added is all child rectangles in every node are ordered by their minimum x value. This can dramatically speed up searching for intersecting rectangles on most modern hardware. 102 | 103 | ### Deleting 104 | 105 | A target rect is searched for from root to the leaf, and if found it's deleted. When there are no more child rects in a node, that node is immedately removed from the tree. 106 | 107 | ### Searching 108 | 109 | Same as the original algorithm. 110 | 111 | ### Splitting 112 | 113 | This is a custom algorithm. 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. 114 | 115 | 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`. 116 | Each child rects is then evaluated to determine which smaller rect it should be placed into. 117 | Two values, `min-dist` and `max-dist`, are calcuated for each child. 118 | 119 | - `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. 120 | - `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. 121 | 122 | When the `min-dist` is less than `max-dist` then the child is placed into the `left` rect. 123 | When the `max-dist` is less than `min-dist` then the child is placed into the `right` rect. 124 | 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. 125 | Each `equal` rect is then one-by-one placed in either `left` or `right`, whichever has less children. 126 | 127 | Finally, sort all the rects in the parent node of the split rect by their 128 | minimum x value. 129 | 130 | ## License 131 | 132 | rtree source code is available under the MIT License. 133 | 134 | -------------------------------------------------------------------------------- /rtree.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Joshua J Baker. All rights reserved. 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package rtree 6 | 7 | import ( 8 | "sync" 9 | "sync/atomic" 10 | "unsafe" 11 | 12 | "github.com/tidwall/geoindex/child" 13 | ) 14 | 15 | // SAFETY: The unsafe package is used, but with care. 16 | // Using "unsafe" allows for one alloction per node and avoids having to use 17 | // an interface{} type for child nodes; that may either be: 18 | // - *leafNode[N,T] 19 | // - *branchNode[N,T] 20 | // This library makes it generally safe by guaranteeing that all references to 21 | // nodes are simply to `*node[N,T]`, which is just the header struct for the 22 | // leaf or branch representation. The difference between a leaf and a branch 23 | // node is that a leaf has an array of item data of generic type T on tail of 24 | // the struct, while a branch has an array of child node pointers on the tail. 25 | // To access the child items `node[N,T].items()` is called; returning a slice, 26 | // or nil if the node is a branch. To access the child nodes 27 | // `node[N,T].children()` is called; returning a slice, or nil if the node is a 28 | // leaf. The `items()` and `children()` methods check the `node[N,T].kind` to 29 | // determine which kind of node it is, which is an enum of `none`, `leaf`, or 30 | // `branch`. The only valid way to create a `*node[N,T]` is 31 | // `RTreeGN[N,T].newNode(leaf bool)` which take a bool that indicates the new 32 | // node kind is a `leaf` or `branch`. 33 | 34 | const maxEntries = 64 35 | const orderBranches = true 36 | const orderLeaves = true 37 | 38 | // copy-on-write atomic incrementer 39 | var gcow uint64 40 | 41 | type numeric interface { 42 | ~int | ~int8 | ~int16 | ~int32 | ~int64 | 43 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | 44 | ~float32 | ~float64 45 | } 46 | 47 | type RTreeGN[N numeric, T any] struct { 48 | icow uint64 49 | count int 50 | rect rect[N] 51 | root *node[N, T] 52 | empty T 53 | qpool *sync.Pool 54 | } 55 | 56 | type rect[N numeric] struct { 57 | min [2]N 58 | max [2]N 59 | } 60 | 61 | func (r *rect[N]) expand(b *rect[N]) { 62 | if b.min[0] < r.min[0] { 63 | r.min[0] = b.min[0] 64 | } 65 | if b.max[0] > r.max[0] { 66 | r.max[0] = b.max[0] 67 | } 68 | if b.min[1] < r.min[1] { 69 | r.min[1] = b.min[1] 70 | } 71 | if b.max[1] > r.max[1] { 72 | r.max[1] = b.max[1] 73 | } 74 | } 75 | 76 | type kind int8 77 | 78 | const ( 79 | none kind = iota 80 | leaf 81 | branch 82 | ) 83 | 84 | type node[N numeric, T any] struct { 85 | icow uint64 86 | kind kind 87 | count int16 88 | rects [maxEntries]rect[N] 89 | } 90 | 91 | func (n *node[N, T]) leaf() bool { 92 | return n.kind == leaf 93 | } 94 | 95 | type leafNode[N numeric, T any] struct { 96 | node[N, T] 97 | items [maxEntries]T 98 | } 99 | 100 | type branchNode[N numeric, T any] struct { 101 | node[N, T] 102 | children [maxEntries]*node[N, T] 103 | } 104 | 105 | func (n *node[N, T]) children() []*node[N, T] { 106 | if n.kind != branch { 107 | // not a branch 108 | return nil 109 | } 110 | return (*branchNode[N, T])(unsafe.Pointer(n)).children[:] 111 | } 112 | 113 | func (n *node[N, T]) items() []T { 114 | if n.kind != leaf { 115 | // not a leaf 116 | return nil 117 | } 118 | return (*leafNode[N, T])(unsafe.Pointer(n)).items[:] 119 | } 120 | 121 | func (tr *RTreeGN[N, T]) newNode(isleaf bool) *node[N, T] { 122 | if isleaf { 123 | n := &leafNode[N, T]{node: node[N, T]{icow: tr.icow, kind: leaf}} 124 | return (*node[N, T])(unsafe.Pointer(n)) 125 | } else { 126 | n := &branchNode[N, T]{node: node[N, T]{icow: tr.icow, kind: branch}} 127 | return (*node[N, T])(unsafe.Pointer(n)) 128 | } 129 | } 130 | 131 | func (n *node[N, T]) rect() rect[N] { 132 | rect := n.rects[0] 133 | for i := 1; i < int(n.count); i++ { 134 | rect.expand(&n.rects[i]) 135 | } 136 | return rect 137 | } 138 | 139 | // Insert data into tree 140 | func (tr *RTreeGN[N, T]) Insert(min, max [2]N, data T) { 141 | ir := rect[N]{min, max} 142 | if tr.root == nil { 143 | if tr.qpool == nil { 144 | tr.qpool = &sync.Pool{ 145 | New: func() any { return &queue[N, T]{} }, 146 | } 147 | } 148 | tr.root = tr.newNode(true) 149 | tr.rect = ir 150 | } 151 | tr.cow(&tr.root) 152 | split, grown := tr.nodeInsert(&tr.rect, tr.root, &ir, data) 153 | if split { 154 | left := tr.root 155 | right := tr.splitNode(tr.rect, left) 156 | tr.root = tr.newNode(false) 157 | tr.root.rects[0] = left.rect() 158 | tr.root.rects[1] = right.rect() 159 | tr.root.children()[0] = left 160 | tr.root.children()[1] = right 161 | tr.root.count = 2 162 | tr.Insert(min, max, data) 163 | if orderBranches { 164 | tr.root.sort() 165 | } 166 | return 167 | } 168 | if grown { 169 | tr.rect.expand(&ir) 170 | if orderBranches && !tr.root.leaf() { 171 | tr.root.sort() 172 | } 173 | } 174 | tr.count++ 175 | } 176 | 177 | func (tr *RTreeGN[N, T]) splitNode(r rect[N], left *node[N, T], 178 | ) (right *node[N, T]) { 179 | return tr.splitNodeLargestAxisEdgeSnap(r, left) 180 | } 181 | 182 | func (n *node[N, T]) orderToRight(idx int) int { 183 | for idx < int(n.count)-1 && n.rects[idx+1].min[0] < n.rects[idx].min[0] { 184 | n.swap(idx+1, idx) 185 | idx++ 186 | } 187 | return idx 188 | } 189 | 190 | func (n *node[N, T]) orderToLeft(idx int) int { 191 | for idx > 0 && n.rects[idx].min[0] < n.rects[idx-1].min[0] { 192 | n.swap(idx, idx-1) 193 | idx-- 194 | } 195 | return idx 196 | } 197 | 198 | // This operation should not be inlined because it's expensive and rarely 199 | // called outside of heavy copy-on-write situations. Marking it "noinline" 200 | // allows for the parent cowLoad to be inlined. 201 | // go:noinline 202 | func (tr *RTreeGN[N, T]) copy(n *node[N, T]) *node[N, T] { 203 | n2 := tr.newNode(n.leaf()) 204 | *n2 = *n 205 | if n2.leaf() { 206 | copy(n2.items()[:n.count], n.items()[:n.count]) 207 | } else { 208 | copy(n2.children()[:n.count], n.children()[:n.count]) 209 | } 210 | return n2 211 | } 212 | 213 | // cow ensures the provided node is not being shared with other R-trees. 214 | // Performs a copy-on-write, if needed. 215 | func (tr *RTreeGN[N, T]) cow(n **node[N, T]) { 216 | if (*n).icow != tr.icow { 217 | *n = tr.copy(*n) 218 | } 219 | } 220 | 221 | func (n *node[N, T]) rsearch(key N) int { 222 | rects := n.rects[:n.count] 223 | for i := 0; i < len(rects); i++ { 224 | if !(n.rects[i].min[0] < key) { 225 | return i 226 | } 227 | } 228 | return int(n.count) 229 | } 230 | 231 | func (tr *RTreeGN[N, T]) nodeInsert(nr *rect[N], n *node[N, T], ir *rect[N], 232 | data T, 233 | ) (split, grown bool) { 234 | if n.leaf() { 235 | if n.count == maxEntries { 236 | return true, false 237 | } 238 | items := n.items() 239 | index := int(n.count) 240 | if orderLeaves { 241 | index = n.rsearch(ir.min[0]) 242 | copy(n.rects[index+1:int(n.count)+1], n.rects[index:int(n.count)]) 243 | copy(items[index+1:int(n.count)+1], items[index:int(n.count)]) 244 | } 245 | n.rects[index] = *ir 246 | items[index] = data 247 | n.count++ 248 | grown = !nr.contains(ir) 249 | return false, grown 250 | } 251 | 252 | // choose a subtree 253 | rects := n.rects[:n.count] 254 | index := -1 255 | var narea N 256 | // take a quick look for any nodes that contain the rect 257 | for i := 0; i < len(rects); i++ { 258 | if rects[i].contains(ir) { 259 | area := rects[i].area() 260 | if index == -1 || area < narea { 261 | index = i 262 | narea = area 263 | } 264 | } 265 | } 266 | if index == -1 { 267 | index = n.chooseLeastEnlargement(ir) 268 | } 269 | 270 | children := n.children() 271 | tr.cow(&children[index]) 272 | split, grown = tr.nodeInsert(&n.rects[index], children[index], ir, data) 273 | if split { 274 | if n.count == maxEntries { 275 | return true, false 276 | } 277 | // split the child node 278 | left := children[index] 279 | right := tr.splitNode(n.rects[index], left) 280 | n.rects[index] = left.rect() 281 | if orderBranches { 282 | copy(n.rects[index+2:int(n.count)+1], 283 | n.rects[index+1:int(n.count)]) 284 | copy(children[index+2:int(n.count)+1], 285 | children[index+1:int(n.count)]) 286 | n.rects[index+1] = right.rect() 287 | children[index+1] = right 288 | n.count++ 289 | if n.rects[index].min[0] > n.rects[index+1].min[0] { 290 | n.swap(index+1, index) 291 | } 292 | index++ 293 | _ = n.orderToRight(index) 294 | } else { 295 | n.rects[n.count] = right.rect() 296 | children[n.count] = right 297 | n.count++ 298 | } 299 | return tr.nodeInsert(nr, n, ir, data) 300 | } 301 | if grown { 302 | // The child rectangle must expand to accomadate the new item. 303 | n.rects[index].expand(ir) 304 | if orderBranches { 305 | n.orderToLeft(index) 306 | } 307 | grown = !nr.contains(ir) 308 | } 309 | return false, grown 310 | } 311 | 312 | func (r *rect[N]) area() N { 313 | return (r.max[0] - r.min[0]) * (r.max[1] - r.min[1]) 314 | } 315 | 316 | // contains return struct when b is fully contained inside of n 317 | func (r *rect[N]) contains(b *rect[N]) bool { 318 | if b.min[0] < r.min[0] || b.max[0] > r.max[0] { 319 | return false 320 | } 321 | if b.min[1] < r.min[1] || b.max[1] > r.max[1] { 322 | return false 323 | } 324 | return true 325 | } 326 | 327 | // intersects returns true if both rects intersect each other. 328 | func (r *rect[N]) intersects(b *rect[N]) bool { 329 | if b.min[0] > r.max[0] || b.max[0] < r.min[0] { 330 | return false 331 | } 332 | if b.min[1] > r.max[1] || b.max[1] < r.min[1] { 333 | return false 334 | } 335 | return true 336 | } 337 | 338 | func (n *node[N, T]) chooseLeastEnlargement(ir *rect[N]) (index int) { 339 | rects := n.rects[:int(n.count)] 340 | var j = -1 341 | var jenlargement N 342 | var jarea N 343 | for i := 0; i < len(rects); i++ { 344 | // calculate the enlarged area 345 | uarea := rects[i].unionedArea(ir) 346 | area := rects[i].area() 347 | enlargement := uarea - area 348 | if j == -1 || enlargement < jenlargement || 349 | (!(enlargement > jenlargement) && area < jarea) { 350 | j, jenlargement, jarea = i, enlargement, area 351 | } 352 | } 353 | return j 354 | } 355 | 356 | func fmin[N numeric](a, b N) N { 357 | if a < b { 358 | return a 359 | } 360 | return b 361 | } 362 | func fmax[N numeric](a, b N) N { 363 | if a > b { 364 | return a 365 | } 366 | return b 367 | } 368 | 369 | // unionedArea returns the area of two rects expanded 370 | func (r *rect[N]) unionedArea(b *rect[N]) N { 371 | return (fmax(r.max[0], b.max[0]) - fmin(r.min[0], b.min[0])) * 372 | (fmax(r.max[1], b.max[1]) - fmin(r.min[1], b.min[1])) 373 | } 374 | 375 | func (r rect[N]) largestAxis() (axis int) { 376 | if r.max[1]-r.min[1] > r.max[0]-r.min[0] { 377 | return 1 378 | } 379 | return 0 380 | } 381 | 382 | func (tr *RTreeGN[N, T]) splitNodeLargestAxisEdgeSnap(r rect[N], left *node[N, T], 383 | ) (right *node[N, T]) { 384 | axis := r.largestAxis() 385 | right = tr.newNode(left.leaf()) 386 | for i := 0; i < int(left.count); i++ { 387 | minDist := left.rects[i].min[axis] - r.min[axis] 388 | maxDist := r.max[axis] - left.rects[i].max[axis] 389 | if minDist < maxDist { 390 | // stay left 391 | } else { 392 | // move to right 393 | tr.moveRectAtIndexInto(left, i, right) 394 | i-- 395 | } 396 | } 397 | // Make sure that both left and right nodes have at least 398 | // two by moving items into underflowed nodes. 399 | if left.count < 2 { 400 | // reverse sort by min axis 401 | right.sortByAxis(axis, true, false) 402 | for left.count < 2 { 403 | tr.moveRectAtIndexInto(right, int(right.count)-1, left) 404 | } 405 | } else if right.count < 2 { 406 | // reverse sort by max axis 407 | left.sortByAxis(axis, true, true) 408 | for right.count < 2 { 409 | tr.moveRectAtIndexInto(left, int(left.count)-1, right) 410 | } 411 | } 412 | 413 | if (orderBranches && !right.leaf()) || (orderLeaves && right.leaf()) { 414 | // It's not uncommon that the nodes to be already ordered. 415 | if !right.issorted() { 416 | right.sort() 417 | } 418 | if !left.issorted() { 419 | left.sort() 420 | } 421 | } 422 | return right 423 | } 424 | 425 | func (tr *RTreeGN[N, T]) moveRectAtIndexInto(from *node[N, T], index int, 426 | into *node[N, T], 427 | ) { 428 | into.rects[into.count] = from.rects[index] 429 | from.rects[index] = from.rects[from.count-1] 430 | if from.leaf() { 431 | into.items()[into.count] = from.items()[index] 432 | from.items()[index] = from.items()[from.count-1] 433 | from.items()[from.count-1] = tr.empty 434 | } else { 435 | into.children()[into.count] = from.children()[index] 436 | from.children()[index] = from.children()[from.count-1] 437 | from.children()[from.count-1] = nil 438 | } 439 | from.count-- 440 | into.count++ 441 | } 442 | 443 | func (n *node[N, T]) search(target rect[N], 444 | iter func(min, max [2]N, data T) bool, 445 | ) bool { 446 | rects := n.rects[:n.count] 447 | if n.leaf() { 448 | items := n.items() 449 | for i := 0; i < len(rects); i++ { 450 | if rects[i].intersects(&target) { 451 | if !iter(rects[i].min, rects[i].max, items[i]) { 452 | return false 453 | } 454 | } 455 | } 456 | return true 457 | } 458 | children := n.children() 459 | for i := 0; i < len(rects); i++ { 460 | if target.intersects(&rects[i]) { 461 | if !children[i].search(target, iter) { 462 | return false 463 | } 464 | } 465 | } 466 | return true 467 | } 468 | 469 | // Len returns the number of items in tree 470 | func (tr *RTreeGN[N, T]) Len() int { 471 | return tr.count 472 | } 473 | 474 | // Search for items in tree that intersect the provided rectangle 475 | func (tr *RTreeGN[N, T]) Search(min, max [2]N, 476 | iter func(min, max [2]N, data T) bool, 477 | ) { 478 | target := rect[N]{min, max} 479 | if tr.root == nil { 480 | return 481 | } 482 | if target.intersects(&tr.rect) { 483 | tr.root.search(target, iter) 484 | } 485 | } 486 | 487 | // Scane all items in the tree 488 | func (tr *RTreeGN[N, T]) Scan(iter func(min, max [2]N, data T) bool) { 489 | if tr.root != nil { 490 | tr.root.scan(iter) 491 | } 492 | } 493 | 494 | func (n *node[N, T]) scan(iter func(min, max [2]N, data T) bool) bool { 495 | if n.leaf() { 496 | for i := 0; i < int(n.count); i++ { 497 | if !iter(n.rects[i].min, n.rects[i].max, n.items()[i]) { 498 | return false 499 | } 500 | } 501 | } else { 502 | for i := 0; i < int(n.count); i++ { 503 | if !n.children()[i].scan(iter) { 504 | return false 505 | } 506 | } 507 | } 508 | return true 509 | } 510 | 511 | // Copy the tree. 512 | // This is a copy-on-write operation and is very fast because it only performs 513 | // a shadowed copy. 514 | func (tr *RTreeGN[N, T]) Copy() *RTreeGN[N, T] { 515 | tr2 := new(RTreeGN[N, T]) 516 | *tr2 = *tr 517 | tr.icow = atomic.AddUint64(&gcow, 1) 518 | tr2.icow = atomic.AddUint64(&gcow, 1) 519 | return tr2 520 | } 521 | 522 | // swap two rectanlges 523 | func (n *node[N, T]) swap(i, j int) { 524 | n.rects[i], n.rects[j] = n.rects[j], n.rects[i] 525 | if n.leaf() { 526 | n.items()[i], n.items()[j] = n.items()[j], n.items()[i] 527 | } else { 528 | n.children()[i], n.children()[j] = n.children()[j], n.children()[i] 529 | } 530 | } 531 | 532 | func (n *node[N, T]) sortByAxis(axis int, rev, max bool) { 533 | n.qsort(0, int(n.count), axis, rev, max) 534 | } 535 | 536 | func (n *node[N, T]) sort() { 537 | n.qsort(0, int(n.count), 0, false, false) 538 | } 539 | 540 | func (n *node[N, T]) issorted() bool { 541 | rects := n.rects[:n.count] 542 | for i := 1; i < len(rects); i++ { 543 | if rects[i].min[0] < rects[i-1].min[0] { 544 | return false 545 | } 546 | } 547 | return true 548 | } 549 | 550 | func (n *node[N, T]) qsort(s, e int, axis int, rev, max bool) { 551 | nrects := e - s 552 | if nrects < 2 { 553 | return 554 | } 555 | left, right := 0, nrects-1 556 | pivot := nrects / 2 // rand and mod not worth it 557 | n.swap(s+pivot, s+right) 558 | rects := n.rects[s:e] 559 | if !rev { 560 | if !max { 561 | for i := 0; i < len(rects); i++ { 562 | if rects[i].min[axis] < rects[right].min[axis] { 563 | n.swap(s+i, s+left) 564 | left++ 565 | } 566 | } 567 | } else { 568 | for i := 0; i < len(rects); i++ { 569 | if rects[i].max[axis] < rects[right].max[axis] { 570 | n.swap(s+i, s+left) 571 | left++ 572 | } 573 | } 574 | } 575 | } else { 576 | if !max { 577 | for i := 0; i < len(rects); i++ { 578 | if rects[right].min[axis] < rects[i].min[axis] { 579 | n.swap(s+i, s+left) 580 | left++ 581 | } 582 | } 583 | } else { 584 | for i := 0; i < len(rects); i++ { 585 | if rects[right].max[axis] < rects[i].max[axis] { 586 | n.swap(s+i, s+left) 587 | left++ 588 | } 589 | } 590 | } 591 | } 592 | n.swap(s+left, s+right) 593 | n.qsort(s, s+left, axis, rev, max) 594 | n.qsort(s+left+1, e, axis, rev, max) 595 | } 596 | 597 | // Delete data from tree 598 | func (tr *RTreeGN[N, T]) Delete(min, max [2]N, data T) { 599 | tr.delete(min, max, data) 600 | } 601 | 602 | func (tr *RTreeGN[N, T]) delete(min, max [2]N, data T) bool { 603 | ir := rect[N]{min, max} 604 | if tr.root == nil || !tr.rect.contains(&ir) { 605 | return false 606 | } 607 | var reinsert []*node[N, T] 608 | tr.cow(&tr.root) 609 | removed, _ := tr.nodeDelete(&tr.rect, tr.root, &ir, data, &reinsert) 610 | if !removed { 611 | return false 612 | } 613 | tr.count-- 614 | if len(reinsert) > 0 { 615 | for _, n := range reinsert { 616 | tr.count -= n.deepCount() 617 | } 618 | } 619 | if tr.count == 0 { 620 | tr.root = nil 621 | tr.rect.min = [2]N{0, 0} 622 | tr.rect.max = [2]N{0, 0} 623 | } else { 624 | for !tr.root.leaf() && tr.root.count == 1 { 625 | tr.root = tr.root.children()[0] 626 | } 627 | } 628 | if len(reinsert) > 0 { 629 | for i := range reinsert { 630 | tr.nodeReinsert(reinsert[i]) 631 | } 632 | } 633 | return true 634 | } 635 | 636 | func compare[T any](a, b T) bool { 637 | return (interface{})(a) == (interface{})(b) 638 | } 639 | 640 | func (tr *RTreeGN[N, T]) nodeDelete(nr *rect[N], n *node[N, T], ir *rect[N], data T, 641 | reinsert *[]*node[N, T], 642 | ) (removed, shrunk bool) { 643 | rects := n.rects[:n.count] 644 | if n.leaf() { 645 | items := n.items() 646 | for i := 0; i < len(rects); i++ { 647 | if ir.contains(&rects[i]) && compare(items[i], data) { 648 | // found the target item to delete 649 | if orderLeaves { 650 | copy(n.rects[i:n.count], n.rects[i+1:n.count]) 651 | copy(items[i:n.count], items[i+1:n.count]) 652 | } else { 653 | n.rects[i] = n.rects[n.count-1] 654 | items[i] = items[n.count-1] 655 | } 656 | items[len(rects)-1] = tr.empty 657 | n.count-- 658 | shrunk = ir.onedge(nr) 659 | if shrunk { 660 | *nr = n.rect() 661 | } 662 | return true, shrunk 663 | } 664 | } 665 | return false, false 666 | } 667 | children := n.children() 668 | for i := 0; i < len(rects); i++ { 669 | if !rects[i].contains(ir) { 670 | continue 671 | } 672 | crect := rects[i] 673 | tr.cow(&children[i]) 674 | removed, shrunk = tr.nodeDelete(&rects[i], children[i], ir, data, 675 | reinsert) 676 | if !removed { 677 | continue 678 | } 679 | if children[i].count == 0 { 680 | *reinsert = append(*reinsert, children[i]) 681 | if orderBranches { 682 | copy(n.rects[i:n.count], n.rects[i+1:n.count]) 683 | copy(children[i:n.count], children[i+1:n.count]) 684 | } else { 685 | n.rects[i] = n.rects[n.count-1] 686 | children[i] = children[n.count-1] 687 | } 688 | children[n.count-1] = nil 689 | n.count-- 690 | *nr = n.rect() 691 | return true, true 692 | } 693 | if shrunk { 694 | shrunk = !rects[i].equals(&crect) 695 | if shrunk { 696 | *nr = n.rect() 697 | } 698 | if orderBranches { 699 | _ = n.orderToRight(i) 700 | } 701 | } 702 | return true, shrunk 703 | } 704 | return false, false 705 | } 706 | 707 | func (r *rect[N]) equals(b *rect[N]) bool { 708 | return !(r.min[0] < b.min[0] || r.min[0] > b.min[0] || 709 | r.min[1] < b.min[1] || r.min[1] > b.min[1] || 710 | r.max[0] < b.max[0] || r.max[0] > b.max[0] || 711 | r.max[1] < b.max[1] || r.max[1] > b.max[1]) 712 | } 713 | 714 | func (n *node[N, T]) deepCount() int { 715 | if n.leaf() { 716 | return int(n.count) 717 | } 718 | var count int 719 | children := n.children()[:n.count] 720 | for i := 0; i < len(children); i++ { 721 | count += children[i].deepCount() 722 | } 723 | return count 724 | } 725 | 726 | func (tr *RTreeGN[N, T]) nodeReinsert(n *node[N, T]) { 727 | if n.leaf() { 728 | rects := n.rects[:n.count] 729 | items := n.items()[:n.count] 730 | for i := range rects { 731 | tr.Insert(rects[i].min, rects[i].max, items[i]) 732 | } 733 | } else { 734 | children := n.children()[:n.count] 735 | for i := 0; i < len(children); i++ { 736 | tr.nodeReinsert(children[i]) 737 | } 738 | } 739 | } 740 | 741 | // onedge returns true when r is on the edge of b 742 | func (r *rect[N]) onedge(b *rect[N]) bool { 743 | return !(r.min[0] > b.min[0] && r.min[1] > b.min[1] && 744 | r.max[0] < b.max[0] && r.max[1] < b.max[1]) 745 | } 746 | 747 | // Replace an item. 748 | // If the old item does not exist then the new item is not inserted. 749 | func (tr *RTreeGN[N, T]) Replace( 750 | oldMin, oldMax [2]N, oldData T, 751 | newMin, newMax [2]N, newData T, 752 | ) { 753 | if tr.delete(oldMin, oldMax, oldData) { 754 | tr.Insert(newMin, newMax, newData) 755 | } 756 | } 757 | 758 | // Bounds returns the minimum bounding rect 759 | func (tr *RTreeGN[N, T]) Bounds() (min, max [2]N) { 760 | return tr.rect.min, tr.rect.max 761 | } 762 | 763 | func (tr *RTreeGN[N, T]) LeftMost() (min, max [2]N, data T) { 764 | if tr.root == nil { 765 | return 766 | } 767 | return tr.root.minist(0) 768 | } 769 | func (tr *RTreeGN[N, T]) BottomMost() (min, max [2]N, data T) { 770 | if tr.root == nil { 771 | return 772 | } 773 | return tr.root.minist(1) 774 | } 775 | func (tr *RTreeGN[N, T]) RightMost() (min, max [2]N, data T) { 776 | if tr.root == nil { 777 | return 778 | } 779 | return tr.root.maxist(0) 780 | } 781 | 782 | func (tr *RTreeGN[N, T]) TopMost() (min, max [2]N, data T) { 783 | if tr.root == nil { 784 | return 785 | } 786 | return tr.root.maxist(1) 787 | } 788 | 789 | func (n *node[N, T]) minist(dim int) (min, max [2]N, data T) { 790 | var j int 791 | var m N 792 | for i, r := range n.rects[:n.count] { 793 | if i == 0 || r.min[dim] < m { 794 | j, m = i, r.min[dim] 795 | } 796 | } 797 | if n.leaf() { 798 | return n.rects[j].min, n.rects[j].max, n.items()[j] 799 | } 800 | return n.children()[j].minist(dim) 801 | } 802 | 803 | func (n *node[N, T]) maxist(dim int) (min, max [2]N, data T) { 804 | var j int 805 | var m N 806 | for i, r := range n.rects[:n.count] { 807 | if i == 0 || r.max[dim] > m { 808 | j, m = i, r.max[dim] 809 | } 810 | } 811 | if n.leaf() { 812 | return n.rects[j].min, n.rects[j].max, n.items()[j] 813 | } 814 | return n.children()[j].maxist(dim) 815 | } 816 | 817 | // Nearby performs a kNN-type operation on the index. 818 | // It's expected that the caller provides its own the `dist` function, which 819 | // is used to calculate a distance to rectangles and data. 820 | // The `iter` function will return all items from the smallest distance to the 821 | // largest distance. 822 | // 823 | // BoxDist is included with this package for simple box-distance 824 | // calculations. For example, say you want to return the closest items to 825 | // Point(10 20): 826 | // 827 | // tr.Nearby( 828 | // rtree.BoxDist([2]float64{10, 20}, [2]float64{10, 20}, nil), 829 | // func(min, max [2]float64, data int, dist float64) bool { 830 | // return true 831 | // }, 832 | // ) 833 | func (tr *RTreeGN[N, T]) Nearby( 834 | dist func(min, max [2]N, data T, item bool) N, 835 | iter func(min, max [2]N, data T, dist N) bool, 836 | ) { 837 | if tr.root == nil { 838 | return 839 | } 840 | q := tr.qpool.Get().(*queue[N, T]) 841 | defer func() { 842 | *q = (*q)[:0] 843 | tr.qpool.Put(q) 844 | }() 845 | 846 | q.push(qnode[N, T]{ 847 | dist: 0, 848 | rect: tr.rect, 849 | node: tr.root, 850 | }) 851 | for { 852 | qn, ok := q.pop() 853 | if !ok { 854 | return 855 | } 856 | if qn.node == nil { 857 | if !iter(qn.rect.min, qn.rect.max, qn.data, qn.dist) { 858 | return 859 | } 860 | } else { 861 | rects := qn.node.rects[:qn.node.count] 862 | if qn.node.leaf() { 863 | items := qn.node.items()[:qn.node.count] 864 | for i := 0; i < len(items); i++ { 865 | q.push(qnode[N, T]{ 866 | dist: dist(rects[i].min, rects[i].max, items[i], true), 867 | rect: rects[i], 868 | data: items[i], 869 | }) 870 | } 871 | } else { 872 | children := qn.node.children()[:qn.node.count] 873 | for i := 0; i < len(children); i++ { 874 | q.push(qnode[N, T]{ 875 | dist: dist(rects[i].min, rects[i].max, tr.empty, false), 876 | rect: rects[i], 877 | node: children[i], 878 | }) 879 | } 880 | } 881 | } 882 | } 883 | } 884 | 885 | type qnode[N numeric, T any] struct { 886 | dist N // distance to 887 | rect rect[N] // item or node rect 888 | data T // item data (or empty for node) 889 | node *node[N, T] // node (or nil for leaf data) 890 | } 891 | 892 | type queue[N numeric, T any] []qnode[N, T] 893 | 894 | func (q *queue[N, T]) push(node qnode[N, T]) { 895 | *q = append(*q, node) 896 | nodes := *q 897 | i := len(nodes) - 1 898 | parent := (i - 1) / 2 899 | for ; i != 0 && nodes[parent].dist > nodes[i].dist; parent = (i - 1) / 2 { 900 | nodes[parent], nodes[i] = nodes[i], nodes[parent] 901 | i = parent 902 | } 903 | } 904 | 905 | func (q *queue[N, T]) pop() (qnode[N, T], bool) { 906 | nodes := *q 907 | if len(nodes) == 0 { 908 | return qnode[N, T]{}, false 909 | } 910 | var n qnode[N, T] 911 | n, nodes[0] = nodes[0], nodes[len(*q)-1] 912 | nodes = nodes[:len(nodes)-1] 913 | *q = nodes 914 | i := 0 915 | for { 916 | smallest := i 917 | left := i*2 + 1 918 | right := i*2 + 2 919 | if left < len(nodes) && nodes[left].dist <= nodes[smallest].dist { 920 | smallest = left 921 | } 922 | if right < len(nodes) && nodes[right].dist <= nodes[smallest].dist { 923 | smallest = right 924 | } 925 | if smallest == i { 926 | break 927 | } 928 | nodes[smallest], nodes[i] = nodes[i], nodes[smallest] 929 | i = smallest 930 | } 931 | return n, true 932 | } 933 | 934 | // BoxDist performs simple box-distance algorithm on rectangles. 935 | // This is the default algorithm for Nearby. 936 | func BoxDist[N numeric, T any](targetMin, targetMax [2]N, 937 | itemDist func(min, max [2]N, data T) N, 938 | ) (dist func(min, max [2]N, data T, item bool) N) { 939 | targ := rect[N]{targetMin, targetMax} 940 | return func(min, max [2]N, data T, item bool) (dist N) { 941 | if item && itemDist != nil { 942 | return itemDist(min, max, data) 943 | } 944 | return targ.boxDist(&rect[N]{min, max}) 945 | } 946 | } 947 | 948 | func (r *rect[N]) boxDist(b *rect[N]) N { 949 | var dist N 950 | squared := fmax(r.min[0], b.min[0]) - fmin(r.max[0], b.max[0]) 951 | if squared > 0 { 952 | dist += squared * squared 953 | } 954 | squared = fmax(r.min[1], b.min[1]) - fmin(r.max[1], b.max[1]) 955 | if squared > 0 { 956 | dist += squared * squared 957 | } 958 | return dist 959 | } 960 | 961 | // Clear will delete all items. 962 | func (tr *RTreeGN[N, T]) Clear() { 963 | tr.count = 0 964 | tr.rect = rect[N]{} 965 | tr.root = nil 966 | } 967 | 968 | //////////////////////////////////////////////////////////////////////////////// 969 | // Inherited wrapped types 970 | //////////////////////////////////////////////////////////////////////////////// 971 | 972 | type RTreeG[T any] struct { 973 | base RTreeGN[float64, T] 974 | } 975 | 976 | // Insert data into tree 977 | func (tr *RTreeG[T]) Insert(min, max [2]float64, data T) { 978 | tr.base.Insert(min, max, data) 979 | } 980 | 981 | // Len returns the number of items in tree 982 | func (tr *RTreeG[T]) Len() int { 983 | return tr.base.Len() 984 | } 985 | 986 | // Search for items in tree that intersect the provided rectangle 987 | func (tr *RTreeG[T]) Search(min, max [2]float64, 988 | iter func(min, max [2]float64, data T) bool, 989 | ) { 990 | tr.base.Search(min, max, iter) 991 | } 992 | 993 | // Scan all items in the tree 994 | func (tr *RTreeG[T]) Scan(iter func(min, max [2]float64, data T) bool) { 995 | tr.base.Scan(iter) 996 | } 997 | 998 | // Copy the tree. 999 | // This is a copy-on-write operation and is very fast because it only performs 1000 | // a shadowed copy. 1001 | func (tr *RTreeG[T]) Copy() *RTreeG[T] { 1002 | return &RTreeG[T]{*tr.base.Copy()} 1003 | } 1004 | 1005 | // Delete data from tree 1006 | func (tr *RTreeG[T]) Delete(min, max [2]float64, data T) { 1007 | tr.base.Delete(min, max, data) 1008 | } 1009 | 1010 | // Replace an item. 1011 | // If the old item does not exist then the new item is not inserted. 1012 | func (tr *RTreeG[T]) Replace( 1013 | oldMin, oldMax [2]float64, oldData T, 1014 | newMin, newMax [2]float64, newData T, 1015 | ) { 1016 | tr.base.Replace( 1017 | oldMin, oldMax, oldData, 1018 | newMin, newMax, newData, 1019 | ) 1020 | } 1021 | 1022 | // Bounds returns the minimum bounding rect 1023 | func (tr *RTreeG[T]) Bounds() (min, max [2]float64) { 1024 | return tr.base.Bounds() 1025 | } 1026 | 1027 | // children is a utility function that returns all children for parent node. 1028 | // If parent node is nil then the root nodes should be returned. The min, max, 1029 | // data, and items slices all must have the same lengths. And, each element 1030 | // from all slices must be associated. Returns true for `items` when the the 1031 | // item at the leaf level. The reuse buffers are empty length slices that can 1032 | // optionally be used to avoid extra allocations. 1033 | func (tr *RTreeG[T]) children(parent interface{}, reuse []child.Child, 1034 | ) (children []child.Child) { 1035 | children = reuse 1036 | if parent == nil { 1037 | if tr.Len() > 0 { 1038 | // fill with the root 1039 | children = append(children, child.Child{ 1040 | Min: tr.base.rect.min, 1041 | Max: tr.base.rect.max, 1042 | Data: tr.base.root, 1043 | Item: false, 1044 | }) 1045 | } 1046 | } else { 1047 | // fill with child items 1048 | n := parent.(*node[float64, T]) 1049 | for i := 0; i < int(n.count); i++ { 1050 | c := child.Child{ 1051 | Min: n.rects[i].min, Max: n.rects[i].max, Item: n.leaf(), 1052 | } 1053 | if c.Item { 1054 | c.Data = n.items()[i] 1055 | } else { 1056 | c.Data = n.children()[i] 1057 | } 1058 | children = append(children, c) 1059 | } 1060 | } 1061 | return children 1062 | } 1063 | 1064 | // Nearby performs a kNN-type operation on the index. 1065 | // It's expected that the caller provides its own the `dist` function, which 1066 | // is used to calculate a distance to rectangles and data. 1067 | // The `iter` function will return all items from the smallest distance to the 1068 | // largest distance. 1069 | // 1070 | // BoxDist is included with this package for simple box-distance 1071 | // calculations. For example, say you want to return the closest items to 1072 | // Point(10 20): 1073 | // 1074 | // tr.Nearby( 1075 | // rtree.BoxDist([2]float64{10, 20}, [2]float64{10, 20}, nil), 1076 | // func(min, max [2]float64, data int, dist float64) bool { 1077 | // return true 1078 | // }, 1079 | // ) 1080 | func (tr *RTreeG[T]) Nearby( 1081 | dist func(min, max [2]float64, data T, item bool) float64, 1082 | iter func(min, max [2]float64, data T, dist float64) bool, 1083 | ) { 1084 | tr.base.Nearby(dist, iter) 1085 | } 1086 | 1087 | // Clear will delete all items. 1088 | func (tr *RTreeG[T]) Clear() { 1089 | tr.base.Clear() 1090 | } 1091 | 1092 | // Generic RTree 1093 | // Deprecated: use RTreeG 1094 | type Generic[T any] struct { 1095 | RTreeG[T] 1096 | } 1097 | 1098 | func (tr *Generic[T]) Copy() *Generic[T] { 1099 | return &Generic[T]{*tr.RTreeG.Copy()} 1100 | } 1101 | 1102 | type RTree struct { 1103 | base RTreeG[any] 1104 | } 1105 | 1106 | // Insert an item into the structure 1107 | func (tr *RTree) Insert(min, max [2]float64, data interface{}) { 1108 | tr.base.Insert(min, max, data) 1109 | } 1110 | 1111 | // Delete an item from the structure 1112 | func (tr *RTree) Delete(min, max [2]float64, data interface{}) { 1113 | tr.base.Delete(min, max, data) 1114 | } 1115 | 1116 | // Replace an item in the structure. This is effectively just a Delete 1117 | // followed by an Insert. But for some structures it may be possible to 1118 | // optimize the operation to avoid multiple passes 1119 | func (tr *RTree) Replace( 1120 | oldMin, oldMax [2]float64, oldData interface{}, 1121 | newMin, newMax [2]float64, newData interface{}, 1122 | ) { 1123 | tr.base.Replace( 1124 | oldMin, oldMax, oldData, 1125 | newMin, newMax, newData, 1126 | ) 1127 | } 1128 | 1129 | // Search the structure for items that intersects the rect param 1130 | func (tr *RTree) Search( 1131 | min, max [2]float64, 1132 | iter func(min, max [2]float64, data interface{}) bool, 1133 | ) { 1134 | tr.base.Search(min, max, iter) 1135 | } 1136 | 1137 | // Scan iterates through all data in tree in no specified order. 1138 | func (tr *RTree) Scan(iter func(min, max [2]float64, data interface{}) bool) { 1139 | tr.base.Scan(iter) 1140 | } 1141 | 1142 | // Len returns the number of items in tree 1143 | func (tr *RTree) Len() int { 1144 | return tr.base.Len() 1145 | } 1146 | 1147 | // Bounds returns the minimum bounding box 1148 | func (tr *RTree) Bounds() (min, max [2]float64) { 1149 | return tr.base.Bounds() 1150 | } 1151 | 1152 | // Children returns all children for parent node. If parent node is nil 1153 | // then the root nodes should be returned. 1154 | // The reuse buffer is an empty length slice that can optionally be used 1155 | // to avoid extra allocations. 1156 | func (tr *RTree) Children(parent interface{}, reuse []child.Child) (children []child.Child) { 1157 | return tr.base.children(parent, reuse) 1158 | } 1159 | 1160 | // Nearby performs a kNN-type operation on the index. 1161 | // It's expected that the caller provides its own the `dist` function, which 1162 | // is used to calculate a distance to rectangles and data. 1163 | // The `iter` function will return all items from the smallest distance to the 1164 | // largest distance. 1165 | // 1166 | // BoxDist is included with this package for simple box-distance 1167 | // calculations. For example, say you want to return the closest items to 1168 | // Point(10 20): 1169 | // 1170 | // tr.Nearby( 1171 | // rtree.BoxDist([2]float64{10, 20}, [2]float64{10, 20}, nil), 1172 | // func(min, max [2]float64, data int, dist float64) bool { 1173 | // return true 1174 | // }, 1175 | // ) 1176 | func (tr *RTree) Nearby( 1177 | algo func(min, max [2]float64, data interface{}, item bool) (dist float64), 1178 | iter func(min, max [2]float64, data interface{}, dist float64) bool, 1179 | ) { 1180 | tr.base.Nearby(algo, iter) 1181 | } 1182 | 1183 | // Copy the tree. 1184 | // This is a copy-on-write operation and is very fast because it only performs 1185 | // a shadowed copy. 1186 | func (tr *RTree) Copy() *RTree { 1187 | return &RTree{base: *tr.base.Copy()} 1188 | } 1189 | 1190 | // Clear will delete all items. 1191 | func (tr *RTree) Clear() { 1192 | tr.base.Clear() 1193 | } 1194 | --------------------------------------------------------------------------------