-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathkd-tree.coffee
110 lines (89 loc) · 2.79 KB
/
kd-tree.coffee
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
class window.KDTree
# Node class for the tree
class Node
constructor: (@obj) ->
@left = null
@right = null
# Instance variables
dimensions = []
k = 0
metric = undefined
distance2 = (a, b) -> Math.pow(a.x - b.x) + Math.pow(a.y - b.y)
distanceMetric = (a, b) ->
sum = 0
sum += Math.pow(a[d] - b[d], 2) for d in dimensions
sum
distanceReal = (a, b) -> Math.sqrt(distanceMetric(a, b))
buildTree = (points, depth) ->
best = null # A max-heap containing the best points found in the search
save = (node, query, n) ->
x = node.obj
if best.size() >= n
return if distanceMetric(x, query) > distanceMetric(best.top(), query)
best.push(x)
best.pop() if best.size() > n
nearestSearch = (cur, query, n, depth = 0) ->
# If we have reached a leaf
if cur.left == null and cur.right == null
# Try saving this point and return
save(cur, query, n)
return
# Find the best and second-best child
bestChild = null
otherChild = null
d = dimensions[depth % k]
if cur.left != null and (cur.right == null or query[d] < cur.obj[d])
bestChild = cur.left
otherChild = cur.right
else
bestChild = cur.right
otherChild = cur.left
# Search the side closest to the query point first
nearestSearch(bestChild, query, n, depth + 1)
# Try saving this point
save(cur, query, n)
minDist = Math.abs(query[d] - cur.obj[d]) # A point on the second-best half can't be closer than this
worstOfBest = distanceReal(best.top(), query) # The "worst best" point we've found till now
# If it's still possible to find better points on the second-best half
if otherChild != null and (minDist < worstOfBest or best.size() < n)
nearestSearch(otherChild, query, n, depth + 1)
constructor: (p = [], d = ["x", "y"], m = distance2) ->
points = p; dimensions = d; m = m
@root = null
k = dimensions.length
for point in points
@add(point)
nearest: (query, n = 1) ->
# Initialize best to a max-heap of points sorted by the distance to the query point
best = new Heap( (x) ->
sum = 0
sum += Math.pow(query[d] - x[d], 2) for d in dimensions
sum
)
nearestSearch(@root, query, n)
arr = []
arr.push(best.pop()) until best.size() == 0
arr
add: (point) ->
cur = @root
parent = null
depth = 0
until cur == null
parent = cur
d = dimensions[depth % k]
if point[d] < cur.obj[d]
cur = cur.left
else
cur = cur.right
depth += 1
depth -= 1
d = dimensions[depth % k]
cur = new Node(point)
if parent == null
@root = cur
else
if point[d] < parent.obj[d]
parent.left = cur
else
parent.right = cur
return @root