/tests/tilequadtree.py

https://github.com/totycro/unknown-horizons-quadtree
Python | 166 lines | 115 code | 27 blank | 24 comment | 21 complexity | f769fb18003ae388e52804500b269eb2 MD5 | raw file
  1. #!/usr/bin/env python
  2. # ###################################################
  3. # Copyright (C) 2009 The Unknown Horizons Team
  4. # team@unknown-horizons.org
  5. # This file is part of Unknown Horizons.
  6. #
  7. # Unknown Horizons is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the
  19. # Free Software Foundation, Inc.,
  20. # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  21. # ###################################################
  22. import unittest
  23. import copy
  24. from horizons.util import Point, Rect, Circle
  25. from horizons.util.tilequadtree import TileQuadTree
  26. class _FakeTile(object):
  27. def __init__(self, x, y):
  28. self.x = x
  29. self.y = y
  30. def __str__(self):
  31. return 'FakeTile(x=%s,y=%s)' % (self.x, self.y)
  32. class TestTileQuadTree(unittest.TestCase):
  33. default_fill_coords = [ (2, 2), (2, 3), (2, 4), (3, 2), (3, 3) ]
  34. default_rect = Rect.init_from_topleft_and_size(0,0, 5, 5)
  35. def _createTree(self):
  36. return TileQuadTree(self.default_rect)
  37. def _fillTree(self, tree):
  38. for coords in self.default_fill_coords:
  39. tree.add_tile( _FakeTile(*coords) )
  40. def testInsert(self):
  41. tiles = {}
  42. tiles[(2,2)] = _FakeTile(2, 2)
  43. tiles[(2,3)] = _FakeTile(2, 3)
  44. tiles[(3,2)] = _FakeTile(3, 2)
  45. tiles[(3,3)] = _FakeTile(3, 3)
  46. tiles[(0,0)] = _FakeTile(0, 0)
  47. tree = TileQuadTree(Rect.init_from_topleft_and_size(0,0, 5, 5))
  48. for coord, tile in tiles.iteritems():
  49. self.assertEqual( tree.get_tile(*coord), None )
  50. tree.add_tile(tile)
  51. self.assertEqual( tree.get_tile(*coord), tile )
  52. def testInsertFull(self):
  53. tiles = {}
  54. for coord in self.default_rect.tuple_iter():
  55. tiles[coord] = _FakeTile(coord[0], coord[1])
  56. tree = TileQuadTree(Rect.init_from_topleft_and_size(0,0, 5, 5))
  57. for coord, tile in tiles.iteritems():
  58. self.assertEqual( tree.get_tile(*coord), None )
  59. tree.add_tile(tile)
  60. self.assertEqual( tree.get_tile(*coord), tile )
  61. def testLen(self):
  62. tree = self._createTree()
  63. self._fillTree(tree)
  64. self.assertEqual(len(tree), len(self.default_fill_coords))
  65. def testIter(self):
  66. tree = self._createTree()
  67. self._fillTree(tree)
  68. coords = copy.deepcopy(self.default_fill_coords)
  69. for tile in tree:
  70. coords.remove( (tile.x, tile.y) )
  71. self.assertEqual(len(coords), 0)
  72. def testRadiusCoords(self):
  73. area = Rect.init_from_topleft_and_size(0, 0, 100, 100)
  74. tree = TileQuadTree(area)
  75. tree2 = TileQuadTree(area)
  76. tree2_check = lambda coord : coord[0] % 2 == 0 and coord[0] % 3 == 0 and coord[1] % 5 == 0
  77. for coord in area.tuple_iter():
  78. tree.add_tile( _FakeTile(coord[0], coord[1] ) )
  79. if tree2_check(coord):
  80. tree2.add_tile( _FakeTile(coord[0], coord[1]) )
  81. def get_diff_msg(l1, l2):
  82. msg = 'unequal at radius '+ str(radius)
  83. msg += '\nl1: ' + str(l1)
  84. msg += '\nl2: ' + str(l2)
  85. diff1 = [ i for i in l1 if i not in l2 ]
  86. diff2 = [ i for i in l2 if i not in l1 ]
  87. msg += '\ndiff1: ' + str(diff1)
  88. msg += '\ndiff2: ' + str(diff2)
  89. return msg
  90. def do_test(center, radius):
  91. l1 = []
  92. for tile in tree.get_radius_tiles(center, radius):
  93. l1.append((tile.x, tile.y))
  94. l2 = []
  95. tree.visit_radius_tiles(center, radius, lambda x : l2.append((x.x, x.y)))
  96. l1.sort()
  97. l2.sort()
  98. l3 = sorted(center.get_radius_coordinates(radius, include_self=True))
  99. self.assertEqual(l1, l3, get_diff_msg(l1, l3))
  100. self.assertEqual(l1, l2, get_diff_msg(l1, l2))
  101. def do_test2(center, radius):
  102. l1 = []
  103. for tile in tree2.get_radius_tiles(center, radius):
  104. l1.append((tile.x, tile.y))
  105. l2 = []
  106. tree2.visit_radius_tiles(center, radius, lambda x : l2.append((x.x, x.y)))
  107. l1.sort()
  108. l2.sort()
  109. l3 = [ x for x in sorted(center.get_radius_coordinates(radius, include_self=True)) if tree2_check(x)]
  110. self.assertEqual(l1, l3, get_diff_msg(l1, l3))
  111. self.assertEqual(l1, l2, get_diff_msg(l1, l2))
  112. center = Rect.init_from_topleft_and_size(20, 20, 0, 0)
  113. center2 = Rect.init_from_topleft_and_size(20, 20, 3, 3)
  114. center3 = Rect.init_from_topleft_and_size(20, 20, 5, 2)
  115. print 'checking for correctness'
  116. for radius in xrange(0,15):
  117. do_test(center, radius)
  118. do_test(center2, radius)
  119. do_test(center3, radius)
  120. do_test2(center, radius)
  121. do_test2(center2, radius)
  122. do_test2(center3, radius)
  123. def testRadiusCoordsSpeed(self):
  124. tree = TileQuadTree(Rect.init_from_topleft_and_size(0, 0, 300, 300) )
  125. print 'testing speed (may take a while)'
  126. for x in xrange(0,300):
  127. if x % 20 == 0: print int((float(x)/300)*100)
  128. for y in xrange(0,300):
  129. tree.add_tile(_FakeTile(x, y) )
  130. center = Rect.init_from_topleft_and_size(144,145,10,10)
  131. import cProfile as profile
  132. import tempfile
  133. #outfilename = tempfile.mkstemp(text = True)[1]
  134. #print 'profile to ', outfilename
  135. #profile.runctx( "for i in tree.get_radius_tiles(center, 100): a_app(i)" , globals(), locals(), outfilename)
  136. def cb(x): pass
  137. outfilename = tempfile.mkstemp(text = True)[1]
  138. print 'profile to ', outfilename
  139. profile.runctx( "tree.visit_radius_tiles(center, 120, cb)" , globals(), locals(), outfilename)