You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
4.1 KiB
Python
144 lines
4.1 KiB
Python
import os, json
|
|
from wand.image import Image
|
|
from wand.drawing import Drawing
|
|
from shortuuid import uuid
|
|
from math import sqrt
|
|
|
|
|
|
def split(image, width, height, pieces):
|
|
|
|
image_file = os.path.basename(image)
|
|
name = os.path.splitext(image_file)[0]
|
|
output = f"chaospuzzles/puzzles/{name}"
|
|
|
|
print(f"Creating the {name} folder")
|
|
try:
|
|
os.makedirs(output)
|
|
os.makedirs(f"{output}/pieces")
|
|
os.makedirs(f"{output}/clusters")
|
|
except OSError:
|
|
pass
|
|
|
|
print("Calculating rows and columns... ", end="")
|
|
piece_side, rows, columns = area_to_pieces(width, height, pieces)
|
|
print(f"Done!")
|
|
print(f"{rows * columns} pieces, {rows} rows by {columns} columns")
|
|
|
|
print("Splitting in tiles... ", end="")
|
|
# copy the source image to the puzzles folder and split it into tiles
|
|
with Image(filename=image) as img:
|
|
img.save(filename=f"{output}/{image_file}")
|
|
for y in range(columns):
|
|
for x in range(rows):
|
|
x_start = x * piece_side
|
|
y_start = y * piece_side
|
|
x_end = x_start + piece_side
|
|
y_end = y_start + piece_side
|
|
with img[x_start:x_end, y_start:y_end] as piece:
|
|
piece.save(filename=f"{output}/pieces/{name}-{x}-{y}.jpg")
|
|
print("Done!")
|
|
# create an id for each piece and store it into a pieces.json file and generate the retro image for the puzzle
|
|
|
|
print("Calculating the ID for each piece... ", end="")
|
|
pieces = []
|
|
with Image(width=width, height=height) as retro:
|
|
draw = Drawing()
|
|
draw.font_size = 11
|
|
draw.text_alignment = "center"
|
|
|
|
for x in range(rows):
|
|
row = []
|
|
for y in range(columns):
|
|
|
|
# generate a random ID for each piece
|
|
ID = uuid()[:4]
|
|
|
|
# write the id in the center of each piece
|
|
draw.text(
|
|
int(x * piece_side + piece_side / 2),
|
|
int(y * piece_side + piece_side / 2),
|
|
f"{name}\n{ID}",
|
|
)
|
|
|
|
# store the id in the pieces dictionary
|
|
row.append(ID)
|
|
pieces.append(row)
|
|
print("Done!")
|
|
print("Generating the picture to print behind the puzzle... ", end="")
|
|
draw(retro)
|
|
retro.save(filename=f"{output}/{name}_retro.png")
|
|
print("Done!")
|
|
|
|
print("Writing pieces.json ", end="")
|
|
with open(f"{output}/pieces.json", "w") as file:
|
|
file.write(json.dumps(pieces))
|
|
print("Done!")
|
|
|
|
print("Writing adjacents.json ", end="")
|
|
adjacents = {}
|
|
for x in range(rows):
|
|
for y in range(columns):
|
|
current = pieces[x][y]
|
|
|
|
if y > 0:
|
|
n = pieces[x][y - 1]
|
|
else:
|
|
n = None
|
|
|
|
if y < columns - 1:
|
|
s = pieces[x][y + 1]
|
|
else:
|
|
s = None
|
|
|
|
if x > 0:
|
|
w = pieces[x - 1][y]
|
|
else:
|
|
w = None
|
|
|
|
if x < rows - 1:
|
|
e = pieces[x + 1][y]
|
|
else:
|
|
e = None
|
|
|
|
adjacents[current] = (n, e, s, w)
|
|
|
|
with open(f"{output}/adjacents.json", "w") as file:
|
|
file.write(json.dumps(adjacents))
|
|
print("Done!")
|
|
|
|
# info.json file with all the info about the puzzle
|
|
print("Writing info.json ", end="")
|
|
with open(f"{output}/info.json", "w") as file:
|
|
file.write(
|
|
json.dumps(
|
|
{
|
|
"name": name,
|
|
"rows": rows,
|
|
"columns": columns,
|
|
"width": width,
|
|
"height": height,
|
|
"image": image_file,
|
|
"clusters": [],
|
|
}
|
|
)
|
|
)
|
|
print("Done!")
|
|
|
|
|
|
def area_to_pieces(width, height, pieces):
|
|
area = width * height
|
|
piece_area = area / pieces
|
|
piece_side = sqrt(piece_area)
|
|
pieces_h = int(width / piece_side)
|
|
pieces_v = int(height / piece_side)
|
|
return (int(piece_side), pieces_h, pieces_v)
|
|
|
|
|
|
# test
|
|
split(
|
|
"chaospuzzles/static/img/katamari.jpg",
|
|
3368, # width
|
|
2380, # height
|
|
500, # pieces
|
|
)
|