util.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. # -*- coding: utf-8 -*-
  2. import math
  3. import copy
  4. # square root of 2 for diagonal distance
  5. SQRT2 = math.sqrt(2)
  6. def backtrace(node):
  7. """
  8. Backtrace according to the parent records and return the path.
  9. (including both start and end nodes)
  10. """
  11. path = [(node.x, node.y)]
  12. while node.parent:
  13. node = node.parent
  14. path.append((node.x, node.y))
  15. path.reverse()
  16. return path
  17. def bi_backtrace(node_a, node_b):
  18. """
  19. Backtrace from start and end node, returns the path for bi-directional A*
  20. (including both start and end nodes)
  21. """
  22. path_a = backtrace(node_a)
  23. path_b = backtrace(node_b)
  24. path_b.reverse()
  25. return path_a + path_b
  26. def raytrace(coords_a, coords_b):
  27. line = []
  28. x0, y0 = coords_a
  29. x1, y1 = coords_b
  30. dx = x1 - x0
  31. dy = y1 - y0
  32. t = 0
  33. grid_pos = [x0, y0]
  34. t_for_one = \
  35. abs(1.0 / dx) if dx > 0 else 10000, \
  36. abs(1.0 / dy) if dy > 0 else 10000
  37. frac_start_pos = (x0 + .5) - x0, (y0 + .5) - y0
  38. t_for_next_border = [
  39. (1 - frac_start_pos[0] if dx < 0 else frac_start_pos[0]) * t_for_one[0],
  40. (1 - frac_start_pos[1] if dx < 0 else frac_start_pos[1]) * t_for_one[1]
  41. ]
  42. step = \
  43. 1 if dx >= 0 else -1, \
  44. 1 if dy >= 0 else -1
  45. while t <= 1:
  46. line.append(copy.copy(grid_pos))
  47. index = 0 if t_for_next_border[0] <= t_for_next_border[1] else 1
  48. t = t_for_next_border[index]
  49. t_for_next_border[index] += t_for_one[index]
  50. grid_pos[index] += step[index]
  51. return line
  52. def bresenham(coords_a, coords_b):
  53. '''
  54. Given the start and end coordinates, return all the coordinates lying
  55. on the line formed by these coordinates, based on Bresenham's algorithm.
  56. http://en.wikipedia.org/wiki/Bresenham's_line_algorithm#Simplification
  57. '''
  58. line = []
  59. x0, y0 = coords_a
  60. x1, y1 = coords_b
  61. dx = abs(x1 - x0)
  62. dy = abs(y1 - y0)
  63. sx = 1 if x0 < x1 else -1
  64. sy = 1 if y0 < y1 else -1
  65. err = dx - dy
  66. while True:
  67. line += [[x0, y0]]
  68. if x0 == x1 and y0 == y1:
  69. break
  70. e2 = err * 2
  71. if e2 > -dy:
  72. err = err - dy
  73. x0 = x0 + sx
  74. if e2 < dx:
  75. err = err + dx
  76. y0 = y0 + sy
  77. return line
  78. def expand_path(path):
  79. '''
  80. Given a compressed path, return a new path that has all the segments
  81. in it interpolated.
  82. '''
  83. expanded = []
  84. if len(path) < 2:
  85. return expanded
  86. for i in range(len(path)-1):
  87. expanded += bresenham(path[i], path[i + 1])
  88. expanded += [path[:-1]]
  89. return expanded
  90. def smoothen_path(grid, path, use_raytrace=False):
  91. x0, y0 = path[0]
  92. sx, sy = path[0]
  93. new_path = [[sx, sy]]
  94. interpolate = raytrace if use_raytrace else bresenham
  95. last_valid = path[1]
  96. for coord in path[2:-1]:
  97. line = interpolate([sx, sy], coord)
  98. blocked = False
  99. for test_coord in line[1:]:
  100. if not grid.walkable(test_coord[0], test_coord[1]):
  101. blocked = True
  102. break
  103. if not blocked:
  104. new_path.append(last_valid)
  105. sx, sy = last_valid
  106. last_valid = coord
  107. new_path.append(path[-1])
  108. return new_path