Skip to content

Instantly share code, notes, and snippets.

@edelooff
Created October 1, 2014 21:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edelooff/2fd76fa7980bb10427cd to your computer and use it in GitHub Desktop.
Save edelooff/2fd76fa7980bb10427cd to your computer and use it in GitHub Desktop.
Drawing tileable hexagons with PIL + Aggdraw
"""Module for drawing of randomly colored, tileable hexagons."""
import math
import random
from PIL import Image
from aggdraw import Draw, Brush
class HexagonGenerator(object):
"""Generates coordinates to draw hexagons when called.
Also determines trivia such as pattern width and height and individual row
offsets for the given edge size.
"""
def __init__(self, edge_length):
"""Initializes the generator with the desired hexagon edge length."""
self.edge_length = edge_length
@property
def col_width(self):
"""Calculates and returns the column width."""
return self.edge_length * 3
@property
def row_height(self):
"""Calculates and returns the row height."""
return math.sin(math.pi / 3) * self.edge_length
@property
def pattern_size(self):
"""Returns the pixel size of a repeatable pattern."""
return self.col_width, self.row_height * 2
def __call__(self, row, col):
"""Yields alternating x and y coords to draw a hexagon.
The row and column arguments determine the placement, the edge_length
determines the size of the drawn hexagon.
"""
x = (col + 0.5 * (row % 2)) * self.col_width
y = row * self.row_height
for angle in range(0, 360, 60):
x += math.cos(math.radians(angle)) * self.edge_length
y += math.sin(math.radians(angle)) * self.edge_length
yield x
yield y
def rows(self, canvas_height):
"""Returns the number of rows required to fill the canvas height."""
return int(math.ceil(canvas_height / self.row_height))
def create_canvas(pattern_size, repetitions):
"""Returns an Image that fits the given number of pattern repetitions."""
width, height = pattern_size
canvas_width = int(repetitions * width)
canvas_height = int(round(canvas_width / height) * height)
return Image.new('RGB', (canvas_width, canvas_height), 'white')
def random_color():
"""Returns a random RGB color from a space of 343 options."""
levels = range(32, 256, 32)
return tuple(random.choice(levels) for _ in range(3))
def main(repetitions=2):
"""Draws a series of tiled hexagons and displays them on screen."""
hexagon = HexagonGenerator(40)
image = create_canvas(hexagon.pattern_size, repetitions)
draw = Draw(image)
for row in range(hexagon.rows(image.size[1])):
colors = [random_color() for _ in range(repetitions)]
for column in range(repetitions + 1):
color = colors[column % repetitions]
draw.polygon(list(hexagon(row, column)), Brush(color))
for column, color in enumerate(colors):
draw.polygon(list(hexagon(-1, column)), Brush(color))
draw.flush()
image.show()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment