diff --git a/sprite-optimizer.py b/sprite-optimizer.py index f82a73e..63c6c1f 100755 --- a/sprite-optimizer.py +++ b/sprite-optimizer.py @@ -1,5 +1,16 @@ #!/usr/bin/env python3 +### Readme ################################################################### +# name: sprite-optimizer +# version: 1.1 +# author: Dark-Storm +# license: CeCILL v2.1 +# +# comments: +# lines are stored as arrays of pixels +############################################################################## + + from argparse import ArgumentParser from bresenham import bresenham from PIL import Image, ImageDraw @@ -7,12 +18,12 @@ from random import randint import sys from time import time - rand = lambda: randint(0,200) progress = False def print_stats(img): + """Print number and percentage of black pixels""" pixels = img.getdata() count = sum([1 for i in pixels if i == 0]) if progress: @@ -20,6 +31,7 @@ def print_stats(img): def get_lines(img): + """Generate all potential lines the image contains""" lines = [] pixels = [(x, y) for x in range(img.width) for y in range(img.height) if img.getpixel((x, y)) == 0] @@ -27,6 +39,7 @@ def get_lines(img): if progress: print("Get lines:", end = "") + # for each pair of pixels, get the line for a in pixels: for b in pixels: line = list(bresenham(a[0], a[1], b[0], b[1])) @@ -35,6 +48,7 @@ def get_lines(img): if p not in pixels: is_in = False break + # if image contains the line, put it on the array if is_in: lines.append(line) i += 1 @@ -49,11 +63,15 @@ def get_lines(img): def removing_doubles(lines): + """Remove lines that are symetric""" results = [] n = len(lines) if progress: print("Remove duplicated lines:", end = "") + # for each line, see if it's already in the output array + # beware not to change the orientation of the line (bresenham is not symetric) + # TODO: optimize a bit this operation for i, l in enumerate(lines): s = sorted(l) same = False @@ -74,18 +92,23 @@ def removing_doubles(lines): def removing_useless(lines): + """Remove lines that are sub-lines of other ones""" results = [] n = len(lines) if progress: print("Remove useless lines:", end = "") + # for each line, see if there is a line that contains every pixel of it for i, l in enumerate(lines): inclusions = 0 + # others are all lines that are not l others = (x for x in lines if x != l) for k in others: if len(list(set(l).intersection(set(k)))) == len(l): inclusions += 1 break + # or len(l) == 1 : we keep single pixels to complete the image if necessary + # TODO: do some tests to see if it's worth or not if inclusions == 0 or len(l) == 1: results.append((len(l), l)) if progress: @@ -99,16 +122,22 @@ def removing_useless(lines): def get_best_solution(img, lines): + """Compute an optimized solution. The magic part of the algorithm""" px_left = [(x, y) for x in range(img.width) for y in range(img.height) if img.getpixel((x, y)) == 0] results = [] n = len(px_left) if progress: print("Draw:", end = "") + # while the entier image has not been drown while len(px_left): + # define the length of lines lines = [(sum([1 for p in l if p in px_left]) - len(l)/(2*max(img.size)), l) for n, l in lines] + # sort them by length lines = sorted(lines) + # pop the longest (p, line) = lines.pop() + # define the pixels that are not covered by any lines px_left = [p for p in px_left if p not in line] results.append((line[0], line[-1])) if progress: @@ -122,13 +151,16 @@ def get_best_solution(img, lines): def generate_code(lines, args): + """Generate Basic Casio code""" str_x, str_y = "{", "{" - for (xa, ya), (x, y) in lines: - x, y = x + int(args.offset[0]), y + int(args.offset[1]) - xa, ya = xa + int(args.offset[0]), ya + int(args.offset[1]) - str_x += "{}, ".format(get_coord(x, xa)) - str_y += "{}, ".format(get_coord(y, ya)) + # Casio's bresenham is reversed compared to classic ones + # so we need to reverse the ends of the lines + for (x_end, y_end), (x_start, y_start) in lines: + x_start, y_start = x_start + int(args.offset[0]), y_start + int(args.offset[1]) + x_end, y_end = x_end + int(args.offset[0]), y_end + int(args.offset[1]) + str_x += "{}, ".format(get_coord(x_start, x_end)) + str_y += "{}, ".format(get_coord(y_start, y_end)) str_x = str_x[:-2] + "}" str_y = str_y[:-2] + "}" @@ -138,6 +170,7 @@ def generate_code(lines, args): # From Zezeombye's BIDE def get_coord(start, end): + """Convert a pair of coordonates to the appropriate x+yT Multi DrawStat output""" result = ""; if start != 0: result += str(start)