This fully threads both the real minimum size of the texture and the dimensions of the texture through to the TA parameters. This also removes spurious zero-area drawing commands (space characters).
173 lines
4.8 KiB
Python
173 lines
4.8 KiB
Python
import sys
|
|
|
|
def from_xy(x: int, y: int) -> int:
|
|
# maximum texture size : 1024x1024
|
|
# maximum 1-dimensional index: 0xfffff
|
|
# bits : 19-0
|
|
|
|
twiddle_ix = 0
|
|
for i in range(0, (19 // 2) + 1):
|
|
twiddle_ix |= ((y >> i) & 1) << (i * 2 + 0)
|
|
twiddle_ix |= ((x >> i) & 1) << (i * 2 + 1)
|
|
|
|
return twiddle_ix
|
|
|
|
assert from_xy(0b000, 0b000) == 0
|
|
assert from_xy(0b001, 0b000) == 2
|
|
assert from_xy(0b010, 0b000) == 8
|
|
assert from_xy(0b011, 0b000) == 10
|
|
assert from_xy(0b100, 0b000) == 32
|
|
assert from_xy(0b101, 0b000) == 34
|
|
assert from_xy(0b110, 0b000) == 40
|
|
assert from_xy(0b111, 0b000) == 42
|
|
|
|
assert from_xy(0b000, 0b001) == 1
|
|
assert from_xy(0b000, 0b010) == 4
|
|
assert from_xy(0b000, 0b011) == 5
|
|
assert from_xy(0b000, 0b100) == 16
|
|
assert from_xy(0b000, 0b101) == 17
|
|
assert from_xy(0b000, 0b110) == 20
|
|
assert from_xy(0b000, 0b111) == 21
|
|
|
|
def from_ix(z_curve_ix: int) -> tuple[int, int]:
|
|
x_y = [0, 0]
|
|
xyi = 0
|
|
while z_curve_ix != 0:
|
|
x_y[(xyi + 1) % 2] |= (z_curve_ix & 1) << (xyi // 2)
|
|
z_curve_ix >>= 1
|
|
xyi += 1
|
|
return tuple(x_y)
|
|
|
|
assert from_ix(17) == (0b000, 0b101)
|
|
assert from_ix(21) == (0b000, 0b111)
|
|
assert from_ix(42) == (0b111, 0b000)
|
|
|
|
"""
|
|
def texture(src: list[int],
|
|
width: int, height: int) -> list[int]:
|
|
dst = [0] * (width * height)
|
|
|
|
for y in range(0, height):
|
|
for x in range(0, width):
|
|
twiddle_ix = from_xy(x, y)
|
|
value = src[y * width + x]
|
|
dst[twiddle_ix] = value
|
|
|
|
return dst
|
|
"""
|
|
|
|
import random
|
|
from sizes import sizes as _sizes
|
|
from colorsys import hsv_to_rgb
|
|
|
|
def all_colors(num):
|
|
for i in range(num):
|
|
hue = i / (num - 1)
|
|
rgb = hsv_to_rgb(hue, 1.0, 1.0)
|
|
def color():
|
|
for i in rgb:
|
|
yield int(i * 255)
|
|
yield tuple(color())
|
|
|
|
def random_colors(num):
|
|
l = list(all_colors(num))
|
|
random.shuffle(l)
|
|
return l
|
|
|
|
def area_pixels(x_off, y_off, width, height):
|
|
for x in range(height):
|
|
for y in range(width):
|
|
px_ix = (x_off + x, y_off + y)
|
|
yield px_ix
|
|
|
|
max_size = (1, 1)
|
|
|
|
def pack_into(texture: dict[tuple[int, int], int],
|
|
width_height: tuple[int, int],
|
|
z_curve_ix: int):
|
|
global max_size
|
|
# ignore passed z_curve_ix
|
|
z_curve_ix = 0
|
|
|
|
width, height = width_height
|
|
if width == 0 or height == 0:
|
|
return (0, 0), z_curve_ix
|
|
|
|
while True:
|
|
x_off, y_off = from_ix(z_curve_ix)
|
|
if x_off >= max_size[0] and y_off >= max_size[0]:
|
|
if max_size[0] == max_size[1]:
|
|
max_size = (max_size[0], max_size[1] * 2)
|
|
else:
|
|
max_size = (max_size[0] * 2, max_size[1])
|
|
z_curve_ix = 0
|
|
|
|
if all((pixel not in texture) and (pixel[0] < max_size[0] and pixel[1] < max_size[1])
|
|
for pixel in area_pixels(x_off, y_off, width, height)):
|
|
#x, y = x_off + width - 1, y_off + height - 1
|
|
return (x_off, y_off), z_curve_ix
|
|
else:
|
|
z_curve_ix += 1
|
|
|
|
def sort_by_area(width_height):
|
|
width, height = width_height
|
|
area = width * height
|
|
return area
|
|
|
|
max_ix = 0
|
|
|
|
def insert_into_texture(texture, color_ix, x_off__y_off, width_height):
|
|
global max_ix
|
|
x_off, y_off = x_off__y_off
|
|
width, height = width_height
|
|
for px_ix in area_pixels(x_off, y_off, width, height):
|
|
assert px_ix not in texture, px_ix
|
|
ix = from_xy(*px_ix)
|
|
if ix > max_ix:
|
|
max_ix = ix
|
|
texture[px_ix] = color_ix
|
|
|
|
def pack_all(sizes):
|
|
global max_size
|
|
max_size = (1, 1)
|
|
global max_ix
|
|
max_ix = 0
|
|
|
|
sorted_sizes = sorted(sizes, key=sort_by_area, reverse=True)
|
|
z_curve_ix = 0
|
|
texture = dict()
|
|
for color_ix, width_height in enumerate(sorted_sizes):
|
|
x_off__y_off, z_curve_ix = pack_into(texture, width_height, z_curve_ix)
|
|
insert_into_texture(texture, color_ix, x_off__y_off, width_height)
|
|
#if color_ix == 2:
|
|
# break
|
|
return texture
|
|
|
|
def ppm(texture: dict, num_colors):
|
|
colors = random_colors(num_colors)
|
|
max_x = max(px[0] for px in texture.keys())
|
|
max_y = max(px[1] for px in texture.keys())
|
|
width = max_x + 1
|
|
height = max_y + 1
|
|
print(" max xy:", max_x, max_y, file=sys.stderr)
|
|
print("max curve ix:", from_xy(max_x, max_y), file=sys.stderr)
|
|
|
|
yield "P3"
|
|
yield f"{width} {height}"
|
|
yield "255"
|
|
for y in range(0, height):
|
|
for x in range(0, width):
|
|
if (x, y) in texture:
|
|
i = texture[(x, y)]
|
|
color = colors[i]
|
|
yield " ".join(map(str, color))
|
|
else:
|
|
yield "0 0 0"
|
|
|
|
if __name__ == '__main__':
|
|
texture = pack_all((x, y) for (y, x) in _sizes)
|
|
print("ideal size", sum(x * y for x, y in _sizes), file=sys.stderr)
|
|
image = list(ppm(texture, len(_sizes)))
|
|
print("\n".join(image))
|
|
pass
|