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

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
)