Skip to content

Instantly share code, notes, and snippets.

@dragon0
Created December 1, 2017 21:55
Show Gist options
  • Save dragon0/28645cbe72a3de99b0222885f66498c1 to your computer and use it in GitHub Desktop.
Save dragon0/28645cbe72a3de99b0222885f66498c1 to your computer and use it in GitHub Desktop.
Utilities for a hexagonal grid system
#!/usr/bin/env python3
import math
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Point({0.x}, {0.y})'.format(self)
class Cube:
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
def __repr__(self):
return 'Cube({0.x}, {0.y}, {0.z})'.format(self)
def __add__(self, other):
if isinstance(other, Cube):
return Cube(self.x + other.x, self.y + other.y, self.z + other.z)
return NotImplemented
class Hex:
def __init__(self, q=0, r=0):
self.q = q
self.r = r
def __repr__(self):
return 'Hex({0.q}, {0.r})'.format(self)
def __add__(self, other):
if isinstance(other, Hex):
return Hex(self.q + other.q, self.r + other.r)
return NotImplemented
# Rendering
def hex_corner(center, size, i, pointytop=False):
'''
Corner i is at (60° * i) + 30°, size units away from the center.
'''
angle_deg = 60 * i + (30 if pointytop else 0)
angle_rad = math.pi / 180 * angle_deg
return Point(center.x + size * math.cos(angle_rad),
center.y + size * math.sin(angle_rad))
'''
In the vertical orientation, the width of a hexagon is width = size * 2.
The horizontal distance between adjacent hexes is horiz = width * 3/4.
The height of a hexagon is height = sqrt(3)/2 * width.
The vertical distance between adjacent hexes is vert = height.
In the horizontal orientation, the height of a hexagon is height = size * 2.
The vertical distance between adjacent hexes is vert = height * 3/4.
The width of a hexagon is width = sqrt(3)/2 * height.
The horizontal distance between adjacent hexes is horiz = width.
'''
def hex_size(size):
width = size * 2
hdist = width * 3/4
height = sqrt(3)/2 * width
vdist = height
return width, height, hdist, vdist
def hex_size_vert(size):
height, width, vdist, hdist = hex_size(size)
return width, height, hdist, vdist
# Representing
'''
The axial coordinate system, sometimes called “trapezoidal”,
is built by taking two of the three coordinates from a cube coordinate system.
Since we have a constraint such as x + y + z = 0,
the third coordinate is redundant.
I’m going to pick two variables, q = x and r = z.
You can think of q as “column” and r as “row”.
# convert cube to axial
q = x
r = z
# convert axial to cube
x = q
z = r
y = -x-z
'''
# Neighbors
cube_directions = [
Cube(+1, -1, 0), Cube(+1, 0, -1), Cube( 0, +1, -1),
Cube(-1, +1, 0), Cube(-1, 0, +1), Cube( 0, -1, +1)
]
cube_diagonals = [
Cube(+2, -1, -1), Cube(+1, +1, -2), Cube(-1, +2, -1),
Cube(-2, +1, +1), Cube(-1, -1, +2), Cube(+1, -2, +1)
]
def cube_direction(direction):
return directions[direction]
def cube_neighbor(hex, direction):
return hex + cube_direction(direction)
def cube_diagonal_neighbor(hex, direction):
return hex + cube_diagonals[direction]
hex_directions = [
Hex(+1, 0), Hex(+1, -1), Hex( 0, -1),
Hex(-1, 0), Hex(-1, +1), Hex( 0, +1)
]
hex_diagonals = [
Hex(+2, -1), Hex(+1, -2), Hex(-1, -1),
Hex(-2, +1), Hex(-1, +2), Hex(+1, +1)
]
def hex_direction(direction):
return directions[direction]
def hex_neighbor(hex, direction):
return hex + hex_direction(direction)
def hex_diagonal_neighbor(hex, direction):
return hex + hex_diagonals[direction]
# Distances
def cube_distance(a, b):
return (abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z)) // 2
def hex_distance(a, b):
return (abs(a.q - b.q)
+ abs(a.q + a.r - b.q - b.r)
+ abs(a.r - b.r)) // 2
#
def cube_lerp(a, b, t):
return Cube(a.x + (b.x - a.x) * t,
a.y + (b.y - a.y) * t,
a.z + (b.z - a.z) * t)
def cube_linedraw(a, b):
N = cube_distance(a, b)
results = []
for i in range(N+1):
results.append(cube_round(cube_lerp(a, b, 1.0/N * i)))
return results
def cube_round(h):
rx = round(h.x)
ry = round(h.y)
rz = round(h.z)
x_diff = abs(rx - h.x)
y_diff = abs(ry - h.y)
z_diff = abs(rz - h.z)
if x_diff > y_diff and x_diff > z_diff:
rx = -ry-rz
elif y_diff > z_diff:
ry = -rx-rz
else:
rz = -rx-ry
return Cube(rx, ry, rz)
def hex_range(center, N):
results = []
for dx in range(-N, N+1):
for dy in range(max(-N, -dx-N), min(N, -dx+N)+1):
dz = -dx-dy
results.append(center + Cube(dx, dy, dz))
return results
'''
# # # - - #
- - - # - #
# # # - # #
'''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment