-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkdbush.go
172 lines (139 loc) · 3.88 KB
/
kdbush.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Package kdbush implements kdbush-tree
package kdbush
// STANDARD_NODE_SIZE default nodeSize kdbush-tree. Higher value means faster indexing but slower search and vice versa
const STANDARD_NODE_SIZE = 64
// KDBush an instance
type KDBush struct {
nodeSize int
ids []int
coords []float64
indexed bool
}
// NewBush return a new pointer of [KDBush]
func NewBush() *KDBush {
kd := KDBush{}
return &kd
}
// BuildIndex build kd-tree index given list of Points
func (kd *KDBush) BuildIndex(points []Point, nodeSize int) *KDBush {
kd.indexed = false
kd.nodeSize = nodeSize
kd.ids = make([]int, len(points))
kd.coords = make([]float64, 2*len(points))
for i, v := range points {
kd.ids[i] = i
kd.coords[i*2] = v.GetX()
kd.coords[i*2+1] = v.GetY()
}
sort(kd.ids, kd.coords, kd.nodeSize, 0, len(kd.ids)-1, 0)
kd.indexed = true
return kd
}
// query helper struct for API Range & Within finding result
type query struct {
left int
right int
axis int
}
// Range returns all indexes points across [minX], [minY], [maxX], [maxY]
func (kd *KDBush) Range(minX, minY, maxX, maxY float64) []int {
if !kd.indexed {
return []int{}
}
stack := []query{{0, len(kd.ids) - 1, 0}}
result := []int{}
var x, y float64
for (len(stack)) > 0 {
left := stack[len(stack)-1].left
right := stack[len(stack)-1].right
axis := stack[len(stack)-1].axis
stack = append(stack[:len(stack)-1], stack[len(stack):]...) // .pop()
// search linearly
if right-left <= kd.nodeSize {
for i := left; i <= right; i++ {
x = kd.coords[2*i]
y = kd.coords[2*i+1]
if x >= minX && x <= maxX && y >= minY && y <= maxY {
result = append(result, kd.ids[i])
}
}
continue
}
// find in the middle index
m := (left + right) >> 1
// include middle item within range
x = kd.coords[2*m]
y = kd.coords[2*m+1]
if x >= minX && x <= maxX && y >= minY && y <= maxY {
result = append(result, kd.ids[m])
}
// queue search in halves that intersect the query
if (axis == 0 && minX <= x) || (axis != 0 && minY <= y) {
stack = append(stack, query{left, m - 1, 1 - axis})
}
if (axis == 0 && maxX >= x) || (axis != 0 && maxY >= y) {
stack = append(stack, query{m + 1, right, 1 - axis})
}
}
return result
}
// Within returns all indexes points within radius of given single [Point]
func (kd *KDBush) Within(qx, qy float64, radius float64) []int {
if !kd.indexed {
return []int{}
}
stack := []query{{0, len(kd.ids) - 1, 0}}
result := []int{}
r2 := radius * radius
var x, y float64
for (len(stack)) > 0 {
left := stack[len(stack)-1].left
right := stack[len(stack)-1].right
axis := stack[len(stack)-1].axis
stack = append(stack[:len(stack)-1], stack[len(stack):]...) // .pop()
// search linearly
if right-left <= kd.nodeSize {
for i := left; i <= right; i++ {
if sqrtDist(kd.coords[2*i], kd.coords[2*i+1], qx, qy) <= r2 {
result = append(result, kd.ids[i])
}
}
continue
}
// find in the middle index
m := (left + right) >> 1
// include the middle item within range
x = kd.coords[2*m]
y = kd.coords[2*m+1]
if sqrtDist(x, y, qx, qy) <= r2 {
result = append(result, kd.ids[m])
}
// queue search in halves that intersect the query
if (axis == 0 && qx-radius <= x) || (axis != 0 && qy-radius <= y) {
stack = append(stack, query{left, m - 1, 1 - axis})
}
if (axis == 0 && qx+radius >= x) || (axis != 0 && qy+radius >= y) {
stack = append(stack, query{m + 1, right, 1 - axis})
}
}
return result
}
//
// Helper get private param
//
// GetNodeSize return current nodesize
func (kd *KDBush) GetNodeSize() int {
return kd.nodeSize
}
// GetIndexed return all kdtree indexes
func (kd *KDBush) GetIndexes() []int {
return kd.ids
}
// GetCoords return all coords
func (kd *KDBush) GetCoords() []float64 {
return kd.coords
}
// Indexed return it's KDBush already indexed or not
func (kd *KDBush) Indexed() bool {
return kd.indexed
}