-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.py
155 lines (140 loc) · 6.86 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
from PIL import Image, ImageDraw
from decimal import *
from itertools import cycle
import re
import math
""" Part one: Color prep."""
def convert():
""" Takes an input of strings and converts it to 3 number values
for RGB.
Return: list of calculated numbers eg [255,344,56]
"""
with open('input.txt', 'r') as myfile:
input = myfile.read().replace('\n', '')
input = re.sub(r'([^\s\w]|_)+', '', input) # sanitise input, @todo, probably needs to tested and improved more
input = input.lower()
input = input.split() # split the input string to a long list ['word','other','other','etc'...]
alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",""]
colorList = []
for word in input:
""" For each individual word, convert the letters to a list.
Each letter will be assigned a value corresponding to it's position
in the alphabet.
"""
letters = list(word) # eg ['w','o','r','d']
number = []
for i,letter in enumerate(letters):
# Only three numbers needed for RGB so use the first three values as a base
# eg: [10, 4, 21]
n = 255.0 / 26.0 # make sure we don't get a value above 255. (z = 26. 25 *10 = 260 but 26 * 9.8 = 248
if i < 3:
# add the first three values to a new list (number)
letterPosition = alphabet.index(letter) * n # *n here to force a good large number base
number.append(letterPosition)
# Use the remaining values to fine tune the first three we have so it will be a better variant
elif i >= 3 and i < 6:
letterPosition = alphabet.index(letter) * n / 26 # /26 here to get a smaller number to add (we don't want (20*10 + 20*10))
number.append(letterPosition)
# For words above six letter, add a further division so we can keep adding values and never reach above 255,
elif i >= 6:
letterPosition = alphabet.index(letter) * n / 26 / 26 # /26/26 to get even smaller numbers probably overkill but it makes sense mathematically.
number.append(letterPosition)
# split the first 3 large values, and smaller 'addition' values to two lists
colors = []
additions = []
for i,val in enumerate(number):
# shift the first three to colors list
if i < 3:
colors.append(val)
else:
additions.append(val)
# We now have 2 lists,eg: colors['35,'67',77'], additions['2,'2','6','0.34','0.43525'...]
# so for each val left in the additions list, we want to add it to the colors, one by one.
# For i in colors, keep adding but only in this order i[0] i[1] i[2]
for i, j in zip(cycle(range(len(colors))), additions):
colors[i] += j
colors[i] = int(colors[i])
for i,v in enumerate(colors):
if v:
colors[i] = int(v)
else:
colors[i] = 255
# some words are less than 3 letters, eg: at, a, is, so add values for these to get colors.
if len(colors) < 2:
colors.append(255)
if len(colors) < 3:
colors.append(255)
colorList.append(colors)
return colorList # final list to work with
""" Part 2: Sizing
There are a few things to work out as we want no limit of words
and an adjustable canvas size. So if there are 2 words, we want the
square to fill the canvas, but for many words the squares should get
smaller and respect the canvas dimensions. (see example images)
"""
def calculate_m():
# get a list of possible squared numbers, this give us the number of
# rows and columns to use for the canvas, eg 4 = 2 x 2, 6 = 3 x 3...
# Return: [] of squared numbers up to 10000 (hopefully we don't get that high!)
m = []
for i in range(1,10000):
m.append(i*i)
return(m)
def grid(n,m):
# given the amount of words, work out what grid or M we will need
# 5 words should use 9 (m = 9) a 3 x 3 grid
# Parameters:
# n: a list of elements
# m: a list of squared numbers
word_count = len(n)
for i,m in enumerate(m):
if m - word_count > 0 or m - word_count == 0:
# loop stops when the amount of words can fit in a grid
# eg ((m = 9) - (word_count = 5) = greater than 0 so use 9 (3x3)
return float(m)
break
m = grid(convert(), calculate_m())
def square_size(m, canvas_size):
# length of canvas divide by square of m gives width or number of cols.
# this gives us the width and height of a square on the canvas
# Parameters:
# canvas_size: overall image size.
# m: a list of squared numbers
canvas_sqrt = int(math.sqrt(m))
w = float(canvas_size) / float(canvas_sqrt)
print 'Square width and height: ', float(w)
return w
""" Part 3: Drawing
Now we have colors and a canvas, next comes the drawing.
Tricky bit here is the correct iteration for filling columns
"""
def draw():
# draws the squares ont a canvas
canvas_size = 1000 # change this to required image size (could be input or variable)
squares_per_row = canvas_size / square_size(m,canvas_size) # eg gives 3 squares for a 3x3 grid
im = Image.new('RGB', (canvas_size,canvas_size), color='white') # draw the canvas
draw = ImageDraw.Draw(im)
s = float(square_size(m,canvas_size)) # set s to square size using a float for accuracy
#work out a suitable border width
border_width = int((float(30) * float(s)) / 100)
# if there are 3 squares to a row, we need to count 3 and increase
# the starting drawing point in the order 0,333,666
j = 0 # vertical counter
k = 0 # horizontal counter
for i,v in enumerate(convert()):
""" If there is magic anywhere, it's here, this took a lot of effort!
We want to draw horizontally, but when we reach the end of the canvas
increase vertically by the width of a square, so we have a counter for
vertical, and for horixontal
"""
if i % squares_per_row == 0 and i != 0: # if i is a multiple of squares per row
j += 1 # increments after the squares per row is hit like 0,0,0,1,1,1,2,2,2...
if i % squares_per_row == 0 and i != 0:
k = 0 # increments after the squares per row is hit like 0,1,2,0,1,2...
points = ((k*s,j*s), (k*s, j*s+s),(k*s+s,j*s+s),(k*s+s, j*s)) #set points with incrementing values :/
draw.polygon((points[0], points[1], points[2], points[3]), outline='black', fill = (v[0],v[1],v[2])) # outline='red', fill='blue'
#borders so redraw the same plots with white lines
draw.line((points[0], points[1], points[2], points[3], points[0]), fill="white", width=border_width) # outline='red', fill='blue'
k += 1
im.save('square.jpg') # save the image.
draw()