-
Notifications
You must be signed in to change notification settings - Fork 1
/
draw.py
135 lines (110 loc) · 4 KB
/
draw.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
"""Draw each knot as an array and save as a PNG image.
(Make a subfolder ./knot-mosaic-images/ before running the script.)
To run:
python3 draw.py <m> <n> <logfile> <keep_about_this_many>
where
m is the number of rows in the board,
n is the number of columns in the board,
logfile is the path to the log file written by solve.py, and
keep_about_this_many is the approximate number of randomly chosen
knots to draw.
If keep_about_this_many is not provided, all of the knots in the
log file will be drawn.
For example, for a 5x3 board to draw ~20 knots, the command is
python3 draw.py 5 3 5x3.log 20
Hannah Miller
https://HM0880.github.io/
September 2022
"""
import matplotlib.image as mpl
import matplotlib.pyplot as plt
import numpy as np
import os
import random
import sys
import encode as utils # custom Python file to encode basic constraints
def read_tile_image(k):
"""Return a 2D array of the image of tile k."""
return mpl.imread(f"./tiles/{k}.png")
def read_tiles():
"""Return a dictionary of the tile images to access later."""
return {
0: read_tile_image(0),
1: read_tile_image(1),
2: read_tile_image(2),
3: read_tile_image(3),
4: read_tile_image(4),
5: read_tile_image(5),
6: read_tile_image(6),
7: read_tile_image(7),
8: read_tile_image(8),
9: read_tile_image(9),
10: read_tile_image(10),
}
def draw_one_solution(board, line, tiles, save_root, make_plot=True):
# Initialize an m x n array to hold the image
m = board.m
n = board.n
td = 80 # tile dimension td; all tiles are 80 x 80
img = np.ones((m * td, n * td)) * 0.9 # make grayscale
# Loop
filename = ""
for v in line.split(" "): # variable v
if int(v) > 0: # if the variable is True, draw the tile
i, j, k = utils.var2tup(board, int(v))
r1 = i * td # the image covers rows r1 to r2
c1 = j * td # the image covers columns c1 to c2
r2 = (i + 1) * td
c2 = (j + 1) * td
img[r1:r2, c1:c2] = tiles[k] # add to image
if k == 10: # convert 10 to A to save space in the file name
k = "A"
filename = filename + "{}".format(k)
# Insert a dash to mark each row of the image. Stride length is n
# (i.e., skip by each row so need to move by the number of columns n).
filename = "-".join(filename[q : q + n] for q in range(0, m * n, n))
filename = os.path.join(save_root, filename + ".png")
# Draw the image
if make_plot:
fig, ax = plt.subplots(nrows=1, ncols=1)
plt.imshow(img, cmap="binary_r", interpolation="none")
ax.set_title("{}".format(filename, fontsize=50, family="serif"))
ax.set_xticks([])
ax.set_yticks([])
plt.tight_layout(pad=0.3, w_pad=0.0, h_pad=1.0)
plt.savefig(filename)
plt.close()
print(f"saved to {os.path.abspath(filename)}")
def process_logfile(m, n, logfile, keep_about_this_many, save_root):
tiles = read_tiles()
board = utils.MakeBoard(m, n)
os.makedirs(save_root, exist_ok=True) # make the `save_root` folder
num_lines = 0
with open(logfile) as f:
for line in f:
num_lines += 1
prob = keep_about_this_many / num_lines
with open(logfile) as f:
for line in f:
if random.random() < prob:
# remove leading spaces and trailing newline
line = line.lstrip().strip("\n")
draw_one_solution(board, line, tiles, save_root, make_plot=True)
################################################################
if __name__ == "__main__":
# Get command line arguments A
A = sys.argv
m = int(A[1])
n = int(A[2])
logfile = A[3]
try:
keep_about_this_many = int(A[4])
except:
keep_about_this_many = np.inf
process_logfile(
m,
n,
logfile,
keep_about_this_many,
save_root="./knot-mosaic-images/",
)