mirror of
https://github.com/stevenrobertson/cuburn.git
synced 2025-02-05 11:40:04 -05:00
Parse XML genomes (merged from flockutil)
This commit is contained in:
parent
529bf48982
commit
5d2b4e47dd
145
cuburn/genome.py
145
cuburn/genome.py
@ -1,6 +1,11 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import warnings
|
||||||
|
import xml.parsers.expat
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from code.variations import var_code, var_params
|
||||||
from code.util import crep
|
from code.util import crep
|
||||||
|
|
||||||
class SplEval(object):
|
class SplEval(object):
|
||||||
@ -238,3 +243,143 @@ def json_encode_genome(obj, indent=0):
|
|||||||
return '%.8g' % obj
|
return '%.8g' % obj
|
||||||
raise TypeError("Don't know how to serialize %s of type %s" %
|
raise TypeError("Don't know how to serialize %s of type %s" %
|
||||||
(obj, type(obj)))
|
(obj, type(obj)))
|
||||||
|
|
||||||
|
class XMLGenomeParser(object):
|
||||||
|
"""
|
||||||
|
Parse an XML genome into a list of dictionaries.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.flames = []
|
||||||
|
self._flame = None
|
||||||
|
self.parser = xml.parsers.expat.ParserCreate()
|
||||||
|
self.parser.StartElementHandler = self.start_element
|
||||||
|
self.parser.EndElementHandler = self.end_element
|
||||||
|
|
||||||
|
def parse(self, file):
|
||||||
|
self.parser.ParseFile(file)
|
||||||
|
|
||||||
|
def start_element(self, name, attrs):
|
||||||
|
if name == 'flame':
|
||||||
|
assert self._flame is None
|
||||||
|
self._flame = dict(attrs)
|
||||||
|
self._flame['xforms'] = []
|
||||||
|
self._flame['palette'] = np.zeros((256, 3), dtype=np.uint8)
|
||||||
|
elif name == 'xform':
|
||||||
|
self._flame['xforms'].append(dict(attrs))
|
||||||
|
elif name == 'finalxform':
|
||||||
|
self._flame['finalxform'] = dict(attrs)
|
||||||
|
elif name == 'color':
|
||||||
|
idx = int(attrs['index'])
|
||||||
|
self._flame['palette'][idx] = map(float, attrs['rgb'].split())
|
||||||
|
def end_element(self, name):
|
||||||
|
if name == 'flame':
|
||||||
|
self.flames.append(self._flame)
|
||||||
|
self._flame = None
|
||||||
|
|
||||||
|
def convert_flame(flame):
|
||||||
|
"""
|
||||||
|
Convert an XML flame (as returned by XMLGenomeParser) into a plain dict
|
||||||
|
in cuburn's JSON genome format representing a loop edge.
|
||||||
|
"""
|
||||||
|
cvt = lambda ks: dict((k, float(flame[k])) for k in ks)
|
||||||
|
camera = {
|
||||||
|
'center': dict(zip('xy', map(float, flame['center'].split()))),
|
||||||
|
'scale': float(flame['scale']) / float(flame['size'].split()[0]),
|
||||||
|
'dither_width': float(flame['filter']),
|
||||||
|
'rotation': float(flame['rotate']),
|
||||||
|
'density': 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
info = {}
|
||||||
|
for k, f in [('name', 'name'), ('author_url', 'url'), ('author', 'nick')]:
|
||||||
|
if f in flame:
|
||||||
|
info[k] = flame[f]
|
||||||
|
|
||||||
|
time = dict(frame_width=float(flame.get('temporal_filter_width', 1)),
|
||||||
|
duration=1)
|
||||||
|
|
||||||
|
color = cvt(['brightness', 'gamma'])
|
||||||
|
color.update((k, float(flame.get(k, d))) for k, d in
|
||||||
|
[('highlight_power', -1), ('gamma_threshold', 0.01)])
|
||||||
|
color['vibrance'] = float(flame['vibrancy'])
|
||||||
|
color['background'] = dict(zip('rgb',
|
||||||
|
map(float, flame['background'].split())))
|
||||||
|
color['palette_times'] = "0"
|
||||||
|
pal = palette_encode(flame['palette'])
|
||||||
|
|
||||||
|
de = dict((k, float(flame.get(f, d))) for f, k, d in
|
||||||
|
[('estimator', 'radius', 11),
|
||||||
|
('estimator_minimum', 'minimum', 0),
|
||||||
|
('estimator_curve', 'curve', 0.6)])
|
||||||
|
|
||||||
|
xfs = dict(enumerate(map(convert_xform, flame['xforms'])))
|
||||||
|
if 'finalxform' in flame:
|
||||||
|
xfs['final'] = convert_xform(flame['finalxform'], True)
|
||||||
|
|
||||||
|
return dict(camera=camera, color=color, de=de, xforms=xfs,
|
||||||
|
info=info, time=time, palettes=[pal], link='self')
|
||||||
|
|
||||||
|
def convert_xform(xf, isfinal=False):
|
||||||
|
# TODO: chaos
|
||||||
|
xf = dict(xf)
|
||||||
|
symm = float(xf.pop('symmetry', 0))
|
||||||
|
anim = xf.pop('animate', symm >= 0)
|
||||||
|
out = dict((k, float(xf.pop(k, v))) for k, v in
|
||||||
|
dict(color=0, color_speed=(1-symm)/2, opacity=1).items())
|
||||||
|
if not isfinal:
|
||||||
|
out['density'] = float(xf.pop('weight'))
|
||||||
|
out['affine'] = convert_affine(xf.pop('coefs'), anim)
|
||||||
|
if 'post' in xf and map(float, xf['post'].split()) != [1, 0, 0, 1, 0, 0]:
|
||||||
|
out['post'] = convert_affine(xf.pop('post'))
|
||||||
|
out['variations'] = {}
|
||||||
|
for k in var_code:
|
||||||
|
if k in xf:
|
||||||
|
var = dict(weight=float(xf.pop(k)))
|
||||||
|
for param, default in var_params.get(k, {}).items():
|
||||||
|
var[param] = float(xf.pop('%s_%s' % (k, param), default))
|
||||||
|
out['variations'][k] = var
|
||||||
|
assert not xf, 'Unrecognized parameters remain: ' + xf
|
||||||
|
return out
|
||||||
|
|
||||||
|
def convert_affine(aff, animate=False):
|
||||||
|
xx, yx, xy, yy, xo, yo = map(float, aff.split())
|
||||||
|
# Invert all instances of y (yy is inverted twice)
|
||||||
|
yx, xy, yo = -yx, -xy, -yo
|
||||||
|
|
||||||
|
xa = np.degrees(np.arctan2(yx, xx))
|
||||||
|
ya = np.degrees(np.arctan2(yy, xy))
|
||||||
|
xm = np.hypot(xx, yx)
|
||||||
|
ym = np.hypot(xy, yy)
|
||||||
|
|
||||||
|
angle_between = ya - xa
|
||||||
|
if angle_between < 0:
|
||||||
|
angle_between += 360
|
||||||
|
|
||||||
|
if angle_between < 180:
|
||||||
|
spread = angle_between / 2.0
|
||||||
|
else:
|
||||||
|
spread = -(360-angle_between) / 2.0
|
||||||
|
|
||||||
|
angle = xa + spread
|
||||||
|
if angle < 0:
|
||||||
|
angle += 360.0
|
||||||
|
|
||||||
|
if animate:
|
||||||
|
angle = [0, angle, 1, angle - 360]
|
||||||
|
|
||||||
|
return dict(spread=spread, magnitude={'x': xm, 'y': ym},
|
||||||
|
angle=angle, offset={'x': xo, 'y': yo})
|
||||||
|
|
||||||
|
def convert_file(path):
|
||||||
|
"""Quick one-shot conversion for an XML genome."""
|
||||||
|
p = GenomeParser()
|
||||||
|
p.parse(open(path))
|
||||||
|
if len(p.flames) > 10:
|
||||||
|
warnings.warn("Lot of flames in this file. Sure it's not a "
|
||||||
|
"frame-based animation?")
|
||||||
|
for flame in p.flames:
|
||||||
|
yield convert_flame(flame)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
print '\n\n'.join(map(json_encode_genome, convert_file(sys.argv[1])))
|
||||||
|
Loading…
Reference in New Issue
Block a user