bresenham.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. '''
  2. Created on 7 nov. 2016
  3. Implementation of bresenham algorithm
  4. Compute lines between coordinates in 2d or 3d, on hexagonal or square grid
  5. @author: olinox
  6. '''
  7. from math import sqrt
  8. from core.constants import SQUAREGRID, HEXGRID
  9. def hex_2d_line(x1, y1, x2, y2):
  10. """returns a line from x1,y1 to x2,y2 on an hexagonal grid
  11. line is a list of coordinates"""
  12. if not all(isinstance(c, int) for c in [x1, y1, x2, y2]):
  13. raise TypeError("x1, y1, x2, y2 have to be integers")
  14. if (x1, y1) == (x2, y2):
  15. return [(x1, y1)]
  16. return _brH(x1, y1, x2, y2)
  17. def hex_3d_line(x1, y1, z1, x2, y2, z2):
  18. """returns a line from x1,y1,z1 to x2,y2,z2 on an hexagonal grid
  19. line is a list of coordinates"""
  20. if not all(isinstance(c, int) for c in [x1, y1, z1, x2, y2, z2]):
  21. raise TypeError("x1, y1, z1, x2, y2, z2 have to be integers")
  22. hoLine = hex_2d_line(x1, y1, x2, y2)
  23. if z1 == z2:
  24. return [(x, y, z1) for x, y in hoLine]
  25. else:
  26. zLine = _brC(0, z1, (len(hoLine)-1), z2)
  27. dicZ = {d:[] for d, z in zLine}
  28. for d, z in zLine:
  29. dicZ[d].append(z)
  30. return [(hoLine[d][0], hoLine[d][1], z) for d, liste_z in dicZ.items() for z in liste_z]
  31. def squ_2d_line(x1, y1, x2, y2):
  32. """returns a line from x1,y1 to x2,y2 on an square grid
  33. line is a list of coordinates
  34. """
  35. if not all(isinstance(c, int) for c in [x1, y1, x2, y2]):
  36. raise TypeError("x1, y1, x2, y2 have to be integers")
  37. if (x1, y1) == (x2, y2):
  38. return [(x1, y1)]
  39. return _brC(x1, y1, x2, y2)
  40. def squ_3d_line(x1, y1, z1, x2, y2, z2):
  41. """returns a line from x1,y1,z1 to x2,y2,z2 on an square grid
  42. line is a list of coordinates"""
  43. if not all(isinstance(c, int) for c in [x1, y1, z1, x2, y2, z2]):
  44. raise TypeError("x1, y1, z1, x2, y2, z2 have to be integers")
  45. hoLine = squ_2d_line(x1, y1, x2, y2)
  46. if z1 == z2:
  47. return [(x, y, z1) for x, y in hoLine]
  48. else:
  49. zLine = _brC(0, z1, (len(hoLine)-1), z2)
  50. return [(hoLine[d][0], hoLine[d][1], z) for d, z in zLine]
  51. def line2d(grid, x1, y1, x2, y2):
  52. """returns a line from x1,y1 to x2,y2
  53. grid could be one of the GRIDTYPES values"""
  54. if not all(isinstance(c, int) for c in [x1, y1, x2, y2]):
  55. raise TypeError("x1, y1, x2, y2 have to be integers")
  56. if grid == SQUAREGRID:
  57. return _brH(x1, y1, x2, y2)
  58. elif grid == HEXGRID:
  59. return _brC(x1, y1, x2, y2)
  60. else:
  61. raise ValueError("grid has to be a value from GRIDTYPES")
  62. def ligne3d(grid, x1, y1, z1, x2, y2, z2):
  63. """returns a line from x1,y1,z1 to x2,y2,z2
  64. grid could be one of the GRIDTYPES values"""
  65. if not all(isinstance(c, int) for c in [x1, y1, z1, x2, y2, z2]):
  66. raise TypeError("x1, y1, z1, x2, y2, z2 have to be integers")
  67. hoLine = line2d(grid, x1, y1, x2, y2)
  68. if z1 == z2:
  69. return [(x, y, z1) for x, y in hoLine]
  70. else:
  71. ligneZ = _brC(0, z1, (len(hoLine)-1), z2)
  72. return [(hoLine[d][0], hoLine[d][1], z) for d, z in ligneZ]
  73. def _brC(x1, y1, x2, y2):
  74. """Line Bresenham algorithm for square grid"""
  75. result = []
  76. # DIAGONAL SYMETRY
  77. V = ( abs(y2 - y1) > abs(x2 - x1) )
  78. if V: y1, x1, y2, x2 = x1, y1, x2, y2
  79. # VERTICAL SYMETRY
  80. reversed_sym = (x1 > x2)
  81. if reversed_sym:
  82. x2, y2, x1, y1 = x1, y1, x2, y2
  83. DX = x2 - x1 ; DY = y2 - y1
  84. offset = 0.0
  85. step = 1 if DY > 0 else -1
  86. alpha = ( abs( DY ) / DX )
  87. y = y1
  88. for x in range(x1, x2 + 1):
  89. coord = (y, x) if V else (x, y)
  90. result.append(coord)
  91. offset += alpha
  92. if offset > 0.5:
  93. y += step
  94. offset -= 1.0
  95. if reversed_sym:
  96. result.reverse()
  97. return result
  98. def _brH(x1, y1, x2, y2):
  99. """Line Bresenham algorithm for hexagonal grid"""
  100. reversed_sym = (x1 > x2)
  101. if reversed_sym:
  102. x1, x2 = x2, x1
  103. y1, y2 = y2, y1
  104. if abs(x2 - x1) < (2*abs((y2-y1)) + abs(x2 % 2) - abs(x1 % 1)):
  105. result = _brH_v(x1, y1, x2, y2)
  106. else:
  107. result = _brH_h(x1, y1, x2, y2)
  108. if reversed_sym:
  109. result.reverse()
  110. return result
  111. def _brH_h(x1, y1, x2, y2):
  112. """Line Bresenham algorithm for hexagonal grid (horizontal quadrants)"""
  113. dx = x2 - x1 ; dy = y2 - y1
  114. if (x1 + x2) % 2 == 1:
  115. dy += 0.5 if x1 % 2 == 0 else -0.5
  116. k = dy / dx
  117. pas = 1
  118. result = []
  119. d = 0.0
  120. pos = (x1, y1)
  121. result.append(pos)
  122. while pos != (x2, y2):
  123. d += k*pas
  124. if d > 0:
  125. x, y = pos
  126. if x % 2 == 0:
  127. pos = x + 1, y
  128. else:
  129. pos = x + 1, y + 1
  130. result.append(pos)
  131. d -= 0.5
  132. else:
  133. x, y = pos
  134. if x % 2 == 0:
  135. pos = x + 1, y - 1
  136. else:
  137. pos = x + 1, y
  138. result.append(pos)
  139. d += 0.5
  140. if pos[0] > x2:
  141. result = []
  142. break
  143. return result
  144. def _brH_v(x1, y1, x2, y2):
  145. """Line Bresenham algorithm for hexagonal grid (vertical quadrants)"""
  146. # unit is half the width: u = 0.5773
  147. # half-height is then 0.8860u, or sqrt(3)/2
  148. direction = 1 if y2 > y1 else -1
  149. dx = 1.5 * (x2 - x1)
  150. dy = direction * (y2 - y1)
  151. if (x1 + x2) % 2 == 1:
  152. if x1 % 2 == 0:
  153. dy += direction*0.5
  154. else:
  155. dy -= direction*0.5
  156. k = dx/(dy*sqrt(3))
  157. pas = 0.5*sqrt(3)
  158. result = []
  159. offset = 0.0
  160. pos = (x1, y1)
  161. result.append(pos)
  162. while pos != (x2, y2):
  163. offset += (k*pas)
  164. if offset <= 0.5:
  165. x, y = pos
  166. pos = x, y + direction
  167. result.append(pos)
  168. offset += (k*pas)
  169. else:
  170. x, y = pos
  171. if (x %2 == 0 and direction == 1) or (x % 2 == 1 and direction == -1):
  172. pos = x + 1, y
  173. else:
  174. pos = x + 1, y + direction
  175. result.append(pos)
  176. offset -= 1.5
  177. if direction*pos[1] > direction*y2:
  178. result = []
  179. break
  180. return result