Created
October 1, 2014 21:14
-
-
Save edelooff/2fd76fa7980bb10427cd to your computer and use it in GitHub Desktop.
Drawing tileable hexagons with PIL + Aggdraw
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
"""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