195 lines
5.2 KiB
Plaintext
195 lines
5.2 KiB
Plaintext
Const NULL As Any Ptr = 0
|
|
|
|
Type Point
|
|
coords(2) As Single '3D points
|
|
End Type
|
|
|
|
Type KdNode
|
|
punto As Point
|
|
izda As KdNode Ptr
|
|
dcha As KdNode Ptr
|
|
End Type
|
|
|
|
Type KdTree
|
|
root As KdNode Ptr
|
|
bestNode As KdNode Ptr
|
|
bestDist As Single
|
|
visited As Integer
|
|
dimensions As Integer
|
|
End Type
|
|
|
|
Function Point_Distance(This As Point, pt As Point) As Single
|
|
Dim dist As Single = 0
|
|
For i As Integer = 0 To 2
|
|
Dim d As Single = this.coords(i) - pt.coords(i)
|
|
dist += d * d
|
|
Next
|
|
Return dist
|
|
End Function
|
|
|
|
Function CreateNode(p As Point) As KdNode Ptr
|
|
Dim node As KdNode Ptr = New KdNode
|
|
node->punto = p
|
|
node->izda = NULL
|
|
node->dcha = NULL
|
|
Return node
|
|
End Function
|
|
|
|
Function MakeTree(nodes() As KdNode Ptr, startIdx As Integer, endIdx As Integer, depth As Integer, dimensions As Integer) As KdNode Ptr
|
|
If endIdx <= startIdx Then Return NULL
|
|
|
|
Dim As Integer midIdx = startIdx + (endIdx - startIdx) \ 2
|
|
Dim As Integer axis = depth Mod dimensions
|
|
|
|
For i As Integer = startIdx To endIdx - 1
|
|
For j As Integer = i + 1 To endIdx
|
|
If nodes(i)->punto.coords(axis) > nodes(j)->punto.coords(axis) Then
|
|
Swap nodes(i), nodes(j)
|
|
End If
|
|
Next
|
|
Next
|
|
|
|
nodes(midIdx)->izda = MakeTree(nodes(), startIdx, midIdx, depth + 1, dimensions)
|
|
nodes(midIdx)->dcha = MakeTree(nodes(), midIdx + 1, endIdx, depth + 1, dimensions)
|
|
|
|
Return nodes(midIdx)
|
|
End Function
|
|
|
|
Sub SearchNearest(node As KdNode Ptr, punto As Point, depth As Integer, tree As KdTree Ptr)
|
|
If node = NULL Then Exit Sub
|
|
|
|
tree->visited += 1
|
|
Dim As Single dist = Point_Distance(node->punto, punto)
|
|
|
|
If tree->bestNode = NULL Orelse dist < tree->bestDist Then
|
|
tree->bestDist = dist
|
|
tree->bestNode = node
|
|
End If
|
|
|
|
If tree->bestDist = 0 Then Exit Sub
|
|
|
|
Dim As Integer axis = depth Mod tree->dimensions
|
|
Dim As Single dx = node->punto.coords(axis) - punto.coords(axis)
|
|
|
|
If dx > 0 Then
|
|
SearchNearest(node->izda, punto, depth + 1, tree)
|
|
If dx * dx >= tree->bestDist Then Exit Sub
|
|
SearchNearest(node->dcha, punto, depth + 1, tree)
|
|
Else
|
|
SearchNearest(node->dcha, punto, depth + 1, tree)
|
|
If dx * dx >= tree->bestDist Then Exit Sub
|
|
SearchNearest(node->izda, punto, depth + 1, tree)
|
|
End If
|
|
End Sub
|
|
|
|
Function BuildKdTree(points() As Point, dimensions As Integer) As KdTree Ptr
|
|
Dim As KdTree Ptr tree = New KdTree
|
|
If tree = NULL Then Return NULL
|
|
|
|
tree->dimensions = dimensions
|
|
tree->bestDist = 0
|
|
tree->visited = 0
|
|
tree->root = NULL
|
|
tree->bestNode = NULL
|
|
|
|
Dim nodes(Ubound(points)) As KdNode Ptr
|
|
For i As Integer = 0 To Ubound(points)
|
|
nodes(i) = CreateNode(points(i))
|
|
If nodes(i) = NULL Then Return NULL
|
|
Next
|
|
|
|
tree->root = MakeTree(nodes(), 0, Ubound(nodes), 0, dimensions)
|
|
Return tree
|
|
End Function
|
|
|
|
Function FindNearest(tree As KdTree Ptr, punto As Point) As Point
|
|
Dim As Point result
|
|
|
|
If tree = NULL Orelse tree->root = NULL Then Return result
|
|
|
|
tree->bestNode = NULL
|
|
tree->visited = 0
|
|
tree->bestDist = 0
|
|
|
|
SearchNearest(tree->root, punto, 0, tree)
|
|
|
|
If tree->bestNode <> NULL Then result = tree->bestNode->punto
|
|
|
|
Return result
|
|
End Function
|
|
|
|
Sub TestWikipedia()
|
|
Print "Wikipedia example data:"
|
|
|
|
Dim As Point points(5)
|
|
points(0).coords(0) = 2: points(0).coords(1) = 3
|
|
points(1).coords(0) = 5: points(1).coords(1) = 4
|
|
points(2).coords(0) = 9: points(2).coords(1) = 6
|
|
points(3).coords(0) = 4: points(3).coords(1) = 7
|
|
points(4).coords(0) = 8: points(4).coords(1) = 1
|
|
points(5).coords(0) = 7: points(5).coords(1) = 2
|
|
|
|
Dim As KdTree Ptr tree = BuildKdTree(points(), 2)
|
|
If tree = NULL Then
|
|
Print "Error creating tree"
|
|
End 1
|
|
End If
|
|
|
|
Dim As Point searchPoint
|
|
searchPoint.coords(0) = 9
|
|
searchPoint.coords(1) = 2
|
|
|
|
Dim As Point nearest = FindNearest(tree, searchPoint)
|
|
|
|
Print "Nearest point: (" & nearest.coords(0) & ", " & nearest.coords(1) & ")"
|
|
Print "Distance: " & Sqr(tree->bestDist)
|
|
Print "Nodes visited: " & tree->visited
|
|
|
|
Delete tree
|
|
End Sub
|
|
|
|
Function RandomDouble(min As Single, max As Single) As Single
|
|
Return min + (max - min) * Rnd()
|
|
End Function
|
|
|
|
Function CreateRandomPoint() As Point
|
|
Dim As Point p
|
|
p.coords(0) = RandomDouble(0, 1)
|
|
p.coords(1) = RandomDouble(0, 1)
|
|
p.coords(2) = RandomDouble(0, 1)
|
|
Return p
|
|
End Function
|
|
|
|
Sub TestRandom(count As Integer)
|
|
Print "Random data (" & count & " points):"
|
|
|
|
Dim As Point points(count-1)
|
|
For i As Integer = 0 To count-1
|
|
points(i) = CreateRandomPoint()
|
|
Next
|
|
|
|
Dim As KdTree Ptr tree = BuildKdTree(points(), 3)
|
|
|
|
Dim As Point searchPoint = CreateRandomPoint()
|
|
Dim As Point nearest = FindNearest(tree, searchPoint)
|
|
|
|
Print "Search point : (" & searchPoint.coords(0) & ", " & searchPoint.coords(1) & ", " & searchPoint.coords(2) & ")"
|
|
Print "Nearest point: (" & nearest.coords(0) & ", " & nearest.coords(1) & ", " & nearest.coords(2) & ")"
|
|
Print "Distance: " & Sqr(tree->bestDist)
|
|
Print "Nodes visited: " & tree->visited
|
|
|
|
Delete tree
|
|
End Sub
|
|
|
|
'Main program
|
|
' Original Wikipedia example
|
|
TestWikipedia()
|
|
Print
|
|
' Random tests
|
|
Randomize Timer
|
|
TestRandom(1000)
|
|
Print
|
|
TestRandom(10000)
|
|
|
|
Sleep
|