gline.py 5.7 KB

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