2018-08-19 20:49:32 +00:00
|
|
|
from __future__ import division, print_function
|
2018-01-21 01:11:19 +00:00
|
|
|
|
|
|
|
import axi
|
|
|
|
import numpy as np
|
2018-01-22 15:59:50 +00:00
|
|
|
import os
|
2018-01-21 01:11:19 +00:00
|
|
|
import sys
|
|
|
|
|
2018-08-19 20:49:32 +00:00
|
|
|
|
|
|
|
W, H = 11-2, 14-2
|
|
|
|
DW, DH = axi.A3_SIZE
|
|
|
|
|
|
|
|
NUMBER = '48'
|
|
|
|
TITLE = 'Fifteen Seconds of The Legend of Zelda'
|
2018-01-22 21:23:10 +00:00
|
|
|
LABEL = '#%s' % NUMBER
|
2018-01-22 15:11:43 +00:00
|
|
|
|
2018-08-19 20:49:32 +00:00
|
|
|
COLUMNS = 8
|
|
|
|
SECONDS = 15
|
|
|
|
FRAME_OFFSET = 900
|
|
|
|
MIN_CHANGES = 2
|
2018-01-29 18:44:44 +00:00
|
|
|
UNIQUE = False
|
2018-08-19 20:49:32 +00:00
|
|
|
SIMPLIFY = 5
|
2018-01-29 18:44:44 +00:00
|
|
|
|
|
|
|
def simplify_sparkline(values, n):
|
|
|
|
if not n:
|
|
|
|
return values
|
|
|
|
result = []
|
|
|
|
previous = None
|
|
|
|
for x, y in enumerate(values):
|
|
|
|
if result:
|
|
|
|
window = result[-n:]
|
|
|
|
lo = min(window)
|
|
|
|
hi = max(window)
|
|
|
|
if y >= lo and y <= hi:
|
|
|
|
result.append(result[-1])
|
|
|
|
previous = y
|
|
|
|
continue
|
|
|
|
if previous is not None:
|
|
|
|
result[-1] = previous
|
|
|
|
result.append(y)
|
|
|
|
previous = y
|
|
|
|
return result
|
2018-01-21 01:11:19 +00:00
|
|
|
|
2018-01-22 00:21:08 +00:00
|
|
|
def stack_drawings(ds, spacing=0):
|
|
|
|
result = axi.Drawing()
|
|
|
|
y = 0
|
|
|
|
for d in ds:
|
|
|
|
d = d.origin().translate(-d.width / 2, y)
|
|
|
|
result.add(d)
|
|
|
|
y += d.height + spacing
|
|
|
|
return result
|
|
|
|
|
2018-01-22 15:11:43 +00:00
|
|
|
def title():
|
|
|
|
d = axi.Drawing(axi.text(TITLE, axi.FUTURAM))
|
|
|
|
d = d.scale_to_fit_height(0.25)
|
2018-01-22 00:21:08 +00:00
|
|
|
d = d.move(6, 8.5, 0.5, 1)
|
2018-01-22 15:11:43 +00:00
|
|
|
d = d.join_paths(0.01)
|
|
|
|
return d
|
|
|
|
|
2018-08-19 20:49:32 +00:00
|
|
|
def label(x, y):
|
2018-01-22 15:11:43 +00:00
|
|
|
d = axi.Drawing(axi.text(LABEL, axi.FUTURAL))
|
|
|
|
d = d.scale_to_fit_height(0.125)
|
|
|
|
d = d.rotate(-90)
|
2018-08-19 20:49:32 +00:00
|
|
|
d = d.move(x, y, 1, 1)
|
2018-01-22 00:21:08 +00:00
|
|
|
d = d.join_paths(0.01)
|
|
|
|
return d
|
|
|
|
|
2018-01-21 01:11:19 +00:00
|
|
|
def main():
|
2018-01-22 15:56:16 +00:00
|
|
|
# read file
|
2018-01-21 01:11:19 +00:00
|
|
|
with open(sys.argv[1], 'r') as fp:
|
|
|
|
data = fp.read()
|
2018-01-22 15:56:16 +00:00
|
|
|
|
|
|
|
# strip and split lines
|
2018-01-21 01:11:19 +00:00
|
|
|
lines = data.split('\n')
|
|
|
|
lines = [x.strip() for x in lines]
|
|
|
|
lines = [x.strip(',') for x in lines]
|
|
|
|
lines = filter(None, lines)
|
2018-01-22 15:56:16 +00:00
|
|
|
|
|
|
|
# read values and transpose
|
2018-08-19 20:49:32 +00:00
|
|
|
data = [tuple(map(int, line.split(','))) for line in lines]
|
2018-01-21 01:11:19 +00:00
|
|
|
data = np.transpose(data)
|
2018-08-19 20:49:32 +00:00
|
|
|
print('%d series in file' % len(data))
|
2018-01-21 01:11:19 +00:00
|
|
|
|
2018-01-22 15:56:16 +00:00
|
|
|
# trim to SECONDS worth of data
|
2018-01-21 01:11:19 +00:00
|
|
|
n = len(data[0])
|
2018-01-22 21:23:10 +00:00
|
|
|
m = int(SECONDS * 60 / 2)
|
|
|
|
mid = int(n // 2) + FRAME_OFFSET
|
|
|
|
a = max(0, mid - m)
|
|
|
|
b = min(n, mid + m)
|
2018-01-21 01:11:19 +00:00
|
|
|
data = [x[a:b] for x in data]
|
|
|
|
|
2018-01-22 15:56:16 +00:00
|
|
|
# remove addresses with too few values
|
2018-01-22 21:23:10 +00:00
|
|
|
data = [x for x in data if len(set(x)) > MIN_CHANGES]
|
2018-08-19 20:49:32 +00:00
|
|
|
print('%d series that changed' % len(data))
|
2018-01-21 01:11:19 +00:00
|
|
|
|
2018-01-29 18:44:44 +00:00
|
|
|
# remove duplicate series
|
|
|
|
if UNIQUE:
|
|
|
|
new_data = []
|
|
|
|
seen = set()
|
|
|
|
for x in data:
|
|
|
|
k = tuple(x)
|
|
|
|
if k in seen:
|
|
|
|
continue
|
|
|
|
seen.add(k)
|
|
|
|
new_data.append(x)
|
|
|
|
data = new_data
|
2018-08-19 20:49:32 +00:00
|
|
|
print('%d unique series' % len(data))
|
2018-01-29 18:44:44 +00:00
|
|
|
|
2018-01-31 18:10:54 +00:00
|
|
|
# delete repetitive stuff
|
2018-08-19 20:49:32 +00:00
|
|
|
del data[136:136+8*14]
|
2018-01-31 18:10:54 +00:00
|
|
|
|
2018-01-22 15:56:16 +00:00
|
|
|
# trim so all rows are full
|
2018-01-21 01:11:19 +00:00
|
|
|
data = data[:int((len(data) // COLUMNS) * COLUMNS)]
|
2018-08-19 20:49:32 +00:00
|
|
|
print('%d series after trimming' % len(data))
|
2018-01-22 15:56:16 +00:00
|
|
|
|
2018-08-19 20:49:32 +00:00
|
|
|
print('%d data points each' % len(data[0]))
|
2018-01-21 01:11:19 +00:00
|
|
|
|
2018-01-22 15:56:16 +00:00
|
|
|
# create sparklines in a grid pattern
|
2018-01-21 01:11:19 +00:00
|
|
|
paths = []
|
|
|
|
for i, row in enumerate(data):
|
2018-01-29 18:44:44 +00:00
|
|
|
row = simplify_sparkline(row, SIMPLIFY)
|
2018-01-21 01:11:19 +00:00
|
|
|
r = i // COLUMNS
|
|
|
|
c = i % COLUMNS
|
|
|
|
lo = min(row)
|
|
|
|
hi = max(row)
|
|
|
|
if lo == hi:
|
|
|
|
row = [0 for x in row]
|
|
|
|
else:
|
|
|
|
row = [(x - lo) / float(hi - lo) for x in row]
|
|
|
|
path = []
|
|
|
|
for j, value in enumerate(row):
|
|
|
|
x = (j / len(row) + c * 1.1)
|
2018-02-10 20:51:48 +00:00
|
|
|
y = 1 - value + r * 1.5
|
2018-01-21 01:11:19 +00:00
|
|
|
path.append((x, y))
|
|
|
|
paths.append(path)
|
2018-08-19 20:49:32 +00:00
|
|
|
|
2018-01-21 01:11:19 +00:00
|
|
|
d = axi.Drawing(paths)
|
2018-01-22 00:21:08 +00:00
|
|
|
|
2018-01-22 15:56:16 +00:00
|
|
|
# add title and label and fit to page
|
2018-08-19 20:49:32 +00:00
|
|
|
d = d.scale(W / d.width, (H - 0.5) / d.height)
|
2018-01-22 15:11:43 +00:00
|
|
|
d = stack_drawings([d, title()], 0.25)
|
|
|
|
d = d.rotate(-90)
|
2018-08-19 20:49:32 +00:00
|
|
|
d = d.center(DW, DH)
|
|
|
|
_, _, lx, ly = d.bounds
|
|
|
|
d.add(label(lx, ly))
|
|
|
|
|
|
|
|
d = d.simplify_paths(0.001)
|
2018-01-22 15:11:43 +00:00
|
|
|
|
2018-08-19 20:49:32 +00:00
|
|
|
print(d.bounds)
|
|
|
|
print(d.size)
|
2018-01-22 00:21:08 +00:00
|
|
|
|
2018-01-22 15:56:16 +00:00
|
|
|
# save outputs
|
2018-01-22 21:23:10 +00:00
|
|
|
dirname = 'nes/%s' % NUMBER
|
2018-01-22 15:59:50 +00:00
|
|
|
try:
|
|
|
|
os.makedirs(dirname)
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
d.dump(os.path.join(dirname, 'out.axi'))
|
2018-08-19 20:49:32 +00:00
|
|
|
rotated = d.rotate(90).center(DH, DW)
|
2018-01-22 15:59:50 +00:00
|
|
|
rotated.dump_svg(os.path.join(dirname, 'out.svg'))
|
2018-08-19 20:49:32 +00:00
|
|
|
x0, y0, x1, y1 = rotated.bounds
|
|
|
|
im = rotated.render(bounds=(x0 - 1, y0 - 1, x1 + 1, y1 + 1))
|
2018-01-22 15:59:50 +00:00
|
|
|
im.write_to_png(os.path.join(dirname, 'out.png'))
|
2018-01-21 01:11:19 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|