Created
December 1, 2017 21:55
-
-
Save dragon0/28645cbe72a3de99b0222885f66498c1 to your computer and use it in GitHub Desktop.
Utilities for a hexagonal grid system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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
https://www.redblobgames.com/grids/hexagons/
https://www.redblobgames.com/grids/hexagons/implementation.html