-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSignMaker.py
More file actions
304 lines (268 loc) · 12.1 KB
/
SignMaker.py
File metadata and controls
304 lines (268 loc) · 12.1 KB
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
from PIL import Image, ImageDraw, ImageFont
from spritemapmanager import get_sprite_map
def CreateSign(
line_count,
separators,
symbols_left,
symbols_right,
text_left,
text_right,
arrows_enabled,
arrows,
width=800,
line_height=110
):
"""
Create an airport-style sign with customizable lines, symbols, text, and arrows.
Args:
line_count: Number of lines in the sign
separators: List of line numbers where separators should be placed
symbols_left: Dict mapping line numbers to lists of symbol names (left side)
symbols_right: Dict mapping line numbers to lists of symbol names (right side)
text_left: Dict mapping line numbers to text strings (left side)
text_right: Dict mapping line numbers to text strings (right side)
arrows_enabled: Dict mapping line numbers to [left_enabled, right_enabled] booleans
arrows: Dict mapping line numbers to [left_angle, right_angle] values
width: Sign width in pixels (default: 800)
line_height: Height of each line in pixels (default: 110)
"""
offset_symbols = [[0, 0] for _ in range(line_count)]
# Group lines based on separators
line_groups = []
current_group = []
for line in range(1, line_count + 1):
current_group.append(line)
if line in separators:
line_groups.append(current_group)
current_group = []
if current_group:
line_groups.append(current_group)
# Calculate total height
height = (line_height * line_count) + len(separators)
# Constants
YELLOW = '#FFC200FF'
FONT_PATH = './Frutiger_bold.ttf'
FONT_SIZE = 48
CIRCLE_OFFSET_Y = 23
ARROW_OFFSET_Y = 20
TEXT_OFFSET_Y = 35
SYMBOL_OFFSET_Y = 25
SYMBOL_SPACING = 65
ARROW_WIDTH = 72
# Calculate vertical centering offset for content within line_height
# Standard line height is 110, with content centered around middle
vertical_center_offset = (line_height - 110) // 2
FILLER_START_Y = 64
FILLER_HEIGHT = 68
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
# Load all circle images
circle_images = {
'single': Image.open('./textures/circleSingle.png'),
'up': Image.open('./textures/circleUp.png'),
'down': Image.open('./textures/circleDown.png'),
'full': Image.open('./textures/circleFull.png')
}
arrow_path = {
0: "./textures/arrow0.png",
45: "./textures/arrow45.png",
90: "./textures/arrow90.png",
135: "./textures/arrow135.png",
180: "./textures/arrow180.png",
225: "./textures/arrow225.png",
270: "./textures/arrow270.png",
315: "./textures/arrow315.png"
}
def get_line_group(line_num):
"""Get the group that contains a specific line."""
for group in line_groups:
if line_num in group:
return group
return []
def get_circle_type(line_num, symbol_index, symbols_dict):
"""Determine which circle type to use based on surrounding symbols."""
group = get_line_group(line_num)
has_above = False
has_below = False
# Check for symbol above in the same group
above_line = line_num - 1
if above_line in group and above_line in symbols_dict:
if len(symbols_dict[above_line]) > symbol_index:
has_above = True
# Check for symbol below in the same group
below_line = line_num + 1
if below_line in group and below_line in symbols_dict:
if len(symbols_dict[below_line]) > symbol_index:
has_below = True
# Return appropriate circle type
if has_above and has_below:
return 'full'
elif has_above and not has_below:
return 'up'
elif not has_above and has_below:
return 'down'
else:
return 'single'
# Create image layers
background = Image.new('RGBA', (width, height), YELLOW)
backgroundDraw = ImageDraw.Draw(background)
foreground = Image.new('RGBA', (width, height), (0, 0, 0, 0))
foregroundDraw = ImageDraw.Draw(foreground)
symbolsImage = Image.new('RGBA', (width, height), (0, 0, 0, 0))
# Draw separators
for sep in separators:
y = (sep * line_height) + offset_symbols[sep - 1][1]
backgroundDraw.line([(0, y), (width, y)], fill='black', width=1)
for offset in range(sep, len(offset_symbols)):
offset_symbols[offset][1] += 1
# Draw arrows on the right side
for line, enabled_arrow_list in arrows_enabled.items():
if enabled_arrow_list[1]:
arrow = Image.open(arrow_path[arrows[line][1]])
foreground.paste(arrow, (width - ARROW_WIDTH, ARROW_OFFSET_Y + (line - 1) * line_height + vertical_center_offset), arrow)
# Offset lines with the arrow
for group in line_groups:
if line in group:
for gline in group:
offset_symbols[gline - 1][0] -= ARROW_WIDTH
# Draw symbols and text on the right side
for line in range(1, line_count + 1):
line_index = line - 1
sr_count = 1
if line in symbols_right:
for symbol_index, symbol_name in enumerate(symbols_right[line]):
symbol = get_sprite_map(symbol_name)
circle_type = get_circle_type(line, symbol_index, symbols_right)
circle = circle_images[circle_type]
# Draw black filler between connected symbols
if circle_type in ['full', 'down']:
filler_x = width - (sr_count * SYMBOL_SPACING) + offset_symbols[line_index][0]
filler_y = (line_index * line_height) + offset_symbols[line_index][1] + FILLER_START_Y + vertical_center_offset
foregroundDraw.rectangle(
[(filler_x, filler_y), (filler_x + 63, filler_y + FILLER_HEIGHT)],
fill='black'
)
# Paste circle
foreground.paste(
circle,
(width - (sr_count * SYMBOL_SPACING) + offset_symbols[line_index][0],
CIRCLE_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
circle
)
# Paste symbol
if symbol.size == 1:
symbolsImage.paste(
symbol.img(),
(width - (sr_count * SYMBOL_SPACING) + 11 + offset_symbols[line_index][0],
SYMBOL_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
symbol.img()
)
elif symbol.size == 2:
symbolsImage.paste(
symbol.img(),
(width - (sr_count * SYMBOL_SPACING) - 2 + offset_symbols[line_index][0],
SYMBOL_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
symbol.img()
)
sr_count += 1
# Draw text on the right side
if line in text_right:
l = font.getlength(text_right[line])
if sr_count > 1:
foregroundDraw.text(
(width - ((sr_count - 1) * SYMBOL_SPACING) - 10 - l + offset_symbols[line_index][0],
TEXT_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
text_right[line], font=font, fill='black'
)
else:
foregroundDraw.text(
(width - 10 - l + offset_symbols[line_index][0],
TEXT_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
text_right[line], font=font, fill='black'
)
# Reset horizontal offsets
for offset in offset_symbols:
offset[0] = 0
# Draw arrows on the left side
for line, enabled_arrow_list in arrows_enabled.items():
if enabled_arrow_list[0]:
arrow = Image.open(arrow_path[arrows[line][0]])
foreground.paste(arrow, (0, ARROW_OFFSET_Y + (line - 1) * line_height + vertical_center_offset), arrow)
# Offset lines with the arrow
for group in line_groups:
if line in group:
for gline in group:
if offset_symbols[gline - 1][0] <1:
offset_symbols[gline - 1][0] += ARROW_WIDTH
# Draw symbols and text on the left side
for line in range(1, line_count + 1):
line_index = line - 1
sl_count = 1
if line in symbols_left:
for symbol_index, symbol_name in enumerate(symbols_left[line]):
symbol = get_sprite_map(symbol_name)
circle_type = get_circle_type(line, symbol_index, symbols_left)
circle = circle_images[circle_type]
# Draw black filler between connected symbols
if circle_type in ['full', 'down']:
filler_x = (sl_count - 1) * SYMBOL_SPACING + offset_symbols[line_index][0]
filler_y = (line_index * line_height) + offset_symbols[line_index][1] + FILLER_START_Y + vertical_center_offset
foregroundDraw.rectangle(
[(filler_x, filler_y), (filler_x + 63, filler_y + FILLER_HEIGHT)],
fill='black'
)
# Paste circle
foreground.paste(
circle,
((sl_count - 1) * SYMBOL_SPACING + offset_symbols[line_index][0],
CIRCLE_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
circle
)
# Paste symbol
if symbol.size == 1:
symbolsImage.paste(
symbol.img(),
((sl_count - 1) * SYMBOL_SPACING + 11 + offset_symbols[line_index][0],
SYMBOL_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
symbol.img()
)
elif symbol.size == 2:
symbolsImage.paste(
symbol.img(),
((sl_count - 1) * SYMBOL_SPACING + offset_symbols[line_index][0],
SYMBOL_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
symbol.img()
)
sl_count += 1
# Draw text on the left side
if line in text_left:
l = font.getlength(text_left[line])
if sl_count > 1:
foregroundDraw.text(
(11 + offset_symbols[line_index][0] + (sl_count - 1) * SYMBOL_SPACING,
TEXT_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
text_left[line], font=font, fill='black'
)
else:
foregroundDraw.text(
(11 + offset_symbols[line_index][0],
TEXT_OFFSET_Y + (line_index * line_height) + offset_symbols[line_index][1] + vertical_center_offset),
text_left[line], font=font, fill='black'
)
# Composite and save the final image
output_file = "output.png"
background.paste(foreground, (0, 0), foreground)
background.paste(symbolsImage, (0, 0), symbolsImage)
background.save(output_file)
# Example usage (remove or comment out in production):
# CreateSign(
# line_count=1,
# separators=[],
# symbols_left={1: ["lift"]},
# symbols_right={1: ["toilet", "train"]},
# text_left={1: "Departures"},
# text_right={1: "Ar"},
# arrows_enabled={1: [True, False], 2: [True, True]},
# arrows={1: [0, 0], 2: [0, 45]},
# width=800,
# line_height=90
# )