mirror of
https://github.com/stevenrobertson/cuburn.git
synced 2025-02-05 11:40:04 -05:00
Checkpoint! Renders again. Many fixes outstanding.
This commit is contained in:
parent
9aa1a94aa1
commit
b53f703e6e
@ -260,13 +260,13 @@ haloclip(float4 *pixbuf, const float *denbuf, float gamma) {
|
|||||||
colorcliplib = devlib(deps=[yuvlib], defs=r'''
|
colorcliplib = devlib(deps=[yuvlib], defs=r'''
|
||||||
__global__ void
|
__global__ void
|
||||||
colorclip(float4 *pixbuf, float gamma, float vibrance, float highpow,
|
colorclip(float4 *pixbuf, float gamma, float vibrance, float highpow,
|
||||||
float linrange, float lingam, float3 bkgd)
|
float linrange, float lingam)
|
||||||
{
|
{
|
||||||
GET_IDX(i);
|
GET_IDX(i);
|
||||||
float4 pix = pixbuf[i];
|
float4 pix = pixbuf[i];
|
||||||
|
|
||||||
if (pix.w <= 0) {
|
if (pix.w <= 0) {
|
||||||
pixbuf[i] = make_float4(bkgd.x, bkgd.y, bkgd.z, 0.0f);
|
pixbuf[i] = make_float4(0, 0, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pix.y -= 0.5f * pix.w;
|
pix.y -= 0.5f * pix.w;
|
||||||
@ -321,10 +321,6 @@ colorclip(float4 *pixbuf, float gamma, float vibrance, float highpow,
|
|||||||
pix.y += (1.0f - vibrance) * powf(opix.y, gamma);
|
pix.y += (1.0f - vibrance) * powf(opix.y, gamma);
|
||||||
pix.z += (1.0f - vibrance) * powf(opix.z, gamma);
|
pix.z += (1.0f - vibrance) * powf(opix.z, gamma);
|
||||||
|
|
||||||
pix.x += (1.0f - alpha) * bkgd.x;
|
|
||||||
pix.y += (1.0f - alpha) * bkgd.y;
|
|
||||||
pix.z += (1.0f - alpha) * bkgd.z;
|
|
||||||
|
|
||||||
pix.x = fminf(1.0f, pix.x);
|
pix.x = fminf(1.0f, pix.x);
|
||||||
pix.y = fminf(1.0f, pix.y);
|
pix.y = fminf(1.0f, pix.y);
|
||||||
pix.z = fminf(1.0f, pix.z);
|
pix.z = fminf(1.0f, pix.z);
|
||||||
|
@ -2,16 +2,14 @@ from collections import OrderedDict
|
|||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from cuburn.genome.use import Wrapper, SplineEval
|
||||||
|
|
||||||
import util
|
import util
|
||||||
from util import Template, assemble_code, devlib, binsearchlib, ringbuflib
|
from util import Template, assemble_code, devlib, binsearchlib, ringbuflib
|
||||||
from color import yuvlib
|
from color import yuvlib
|
||||||
from mwc import mwclib
|
from mwc import mwclib
|
||||||
|
|
||||||
class GenomePackerName(str):
|
class PackerWrapper(Wrapper):
|
||||||
"""Class to indicate that a property is precalculated on the device"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class GenomePackerView(object):
|
|
||||||
"""
|
"""
|
||||||
Obtain accessors in generated code.
|
Obtain accessors in generated code.
|
||||||
|
|
||||||
@ -25,47 +23,46 @@ class GenomePackerView(object):
|
|||||||
code and an interpolator for use in generating that code. This conversion
|
code and an interpolator for use in generating that code. This conversion
|
||||||
is done when the property is coerced into a string by the templating
|
is done when the property is coerced into a string by the templating
|
||||||
mechanism, so you can easily nest objects by saying, for instance,
|
mechanism, so you can easily nest objects by saying, for instance,
|
||||||
{{pcp.camera.rotation}} from within templated code. The accessed property
|
{{pcp.camera.rotation}} from within templated code.
|
||||||
must be a SplEval object, or a precalculated value (see
|
|
||||||
``GenomePackerPrecalc``).
|
|
||||||
|
|
||||||
Index operations are converted to property accesses as well, so that you
|
|
||||||
don't have to make a mess with 'getattr' in your code: {{pcp.xforms[x]}}
|
|
||||||
works just fine. This means, however, that no arrays can be packed
|
|
||||||
directly; they must be converted to have string-based keys first, and
|
|
||||||
any loops must be unrolled in your code.
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, packer, ptr_name, wrapped, prefix=()):
|
def __init__(self, packer, val, spec=None, path=()):
|
||||||
self.packer = packer
|
super(PackerWrapper, self).__init__(val, spec)
|
||||||
self.ptr_name = ptr_name
|
self.packer, self.path = packer, path
|
||||||
self.wrapped = wrapped
|
|
||||||
self.prefix = prefix
|
def wrap_dict(self, path, spec, val):
|
||||||
|
return type(self)(self.packer, val, spec, path)
|
||||||
|
|
||||||
|
def wrap_spline(self, path, spec, val):
|
||||||
|
return PackerSpline(self.packer, path, spec)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
w = getattr(self.wrapped, name)
|
path = self.path + (name,)
|
||||||
return type(self)(self.packer, self.ptr_name, w, self.prefix+(name,))
|
if path in self.packer.packed_precalc:
|
||||||
# As with the Genome class, we're all-dict, no-array here
|
return self.packer.devname(path)
|
||||||
__getitem__ = lambda s, n: getattr(s, str(n))
|
return super(PackerWrapper, self).__getattr__(name)
|
||||||
|
|
||||||
|
def _precalc(self):
|
||||||
|
"""Create a GenomePackerPrecalc object. See that class for details."""
|
||||||
|
return PrecalcWrapper(self.packer, self._val, self.spec, self.path)
|
||||||
|
|
||||||
|
class PackerSpline(object):
|
||||||
|
def __init__(self, packer, path, spec):
|
||||||
|
self.packer, self.path, self.spec = packer, path, spec
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
Returns the packed name in a format suitable for embedding directly
|
Returns the packed name in a format suitable for embedding directly
|
||||||
into device code.
|
into device code.
|
||||||
"""
|
"""
|
||||||
# So evil. When the template calls __str__ to format the output, we
|
# When the template calls __str__ to format one of these splines, this
|
||||||
# allocate things. This makes for neater embedded code, which is where
|
# allocates the corresponding spline.
|
||||||
# the real complexity lies, but it also means printf() debugging when
|
return self.packer._require(self.spec, self.path)
|
||||||
# templating will screw with the allocation tables!
|
|
||||||
if not isinstance(self.wrapped, GenomePackerName):
|
|
||||||
self.packer._require(self.prefix)
|
|
||||||
# TODO: verify namespace stomping, etc
|
|
||||||
return '%s.%s' % (self.ptr_name, '_'.join(self.prefix))
|
|
||||||
|
|
||||||
def _precalc(self):
|
class PrecalcSpline(PackerSpline):
|
||||||
"""Create a GenomePackerPrecalc object. See that class for details."""
|
def __str__(self):
|
||||||
return GenomePackerPrecalc(self.packer, self.ptr_name,
|
return self.packer._require_pre(self.spec, self.path)
|
||||||
self.wrapped, self.prefix)
|
|
||||||
|
|
||||||
class GenomePackerPrecalc(GenomePackerView):
|
class PrecalcWrapper(PackerWrapper):
|
||||||
"""
|
"""
|
||||||
Insert precalculated values into the packed genome.
|
Insert precalculated values into the packed genome.
|
||||||
|
|
||||||
@ -91,35 +88,24 @@ class GenomePackerPrecalc(GenomePackerView):
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
def do_precalc(px):
|
def do_precalc(pcam):
|
||||||
pcam = px._precalc()
|
|
||||||
pcam._code(Template('''
|
pcam._code(Template('''
|
||||||
{{pcam._set('prop_sin')}} = sin({{pcam.prop}});
|
{{pcam._set('prop_sin')}} = sin({{pcam.prop}});
|
||||||
''').substitute(pcam=pcam))
|
''').substitute(pcam=pcam))
|
||||||
|
|
||||||
def gen_code(px):
|
def gen_code(px):
|
||||||
return Template('''
|
return Template('''
|
||||||
{{do_precalc(px)}}
|
{{do_precalc(px._precalc())}}
|
||||||
printf("The sin of %g is %g.", {{px.prop}}, {{px.prop_sin}});
|
printf("The sin of %g is %g.", {{px.prop}}, {{px.prop_sin}});
|
||||||
''').substitute(px=px)
|
''').substitute(px=px)
|
||||||
"""
|
"""
|
||||||
def __init__(self, packer, ptr_name, wrapped, prefix):
|
def wrap_spline(self, path, spec, val):
|
||||||
super(GenomePackerPrecalc, self).__init__(packer, 'out', wrapped, prefix)
|
return PrecalcSpline(self.packer, path, spec)
|
||||||
def __str__(self):
|
|
||||||
return self.packer._require_pre(self.prefix)
|
|
||||||
def _magscale(self):
|
|
||||||
"""
|
|
||||||
This is a temporary hack which turns on magnitude scaling for the
|
|
||||||
value on which it is called. Takes the place of __str__ serialization.
|
|
||||||
"""
|
|
||||||
return self.packer._require_pre(self.prefix, True)
|
|
||||||
def _set(self, name):
|
def _set(self, name):
|
||||||
fullname = self.prefix + (name,)
|
path = self.path + (name,)
|
||||||
self.packer._pre_alloc(fullname)
|
return self.packer._pre_alloc(path)
|
||||||
# This just modifies the underlying object, because I'm too lazy right
|
|
||||||
# now to ghost the namespace
|
|
||||||
self.wrapped[name] = GenomePackerName('_'.join(fullname))
|
|
||||||
return '%s->%s' % (self.ptr_name, self.wrapped[name])
|
|
||||||
def _code(self, code):
|
def _code(self, code):
|
||||||
self.packer.precalc_code.append(code)
|
self.packer.precalc_code.append(code)
|
||||||
|
|
||||||
@ -127,26 +113,27 @@ class GenomePacker(object):
|
|||||||
"""
|
"""
|
||||||
Packs a genome for use in iteration.
|
Packs a genome for use in iteration.
|
||||||
"""
|
"""
|
||||||
def __init__(self, tname):
|
def __init__(self, tname, ptr_name, spec):
|
||||||
"""
|
"""
|
||||||
Create a new DataPacker.
|
Create a new DataPacker.
|
||||||
|
|
||||||
``tname`` is the name of the structure typedef that will be emitted
|
``tname`` is the name of the structure typedef that will be emitted
|
||||||
via this object's ``decls`` property.
|
via this object's ``decls`` property.
|
||||||
"""
|
"""
|
||||||
self.tname = tname
|
self.tname, self.ptr_name, self.spec = tname, ptr_name, spec
|
||||||
# We could do this in the order that things are requested, but we want
|
# We could do this in the order that things are requested, but we want
|
||||||
# to be able to treat the direct stuff as a list so this function
|
# to be able to treat the direct stuff as a list so this function
|
||||||
# doesn't unroll any more than it has to. So we separate things into
|
# doesn't unroll any more than it has to. So we separate things into
|
||||||
# direct requests, and those that need precalculation.
|
# direct requests, and those that need precalculation.
|
||||||
# Values of OrderedDict are unused; basically, it's just OrderedSet.
|
# Values of OrderedDict are unused; basically, it's just OrderedSet.
|
||||||
self.packed_direct = OrderedDict()
|
self.packed_direct = OrderedDict()
|
||||||
|
# Feel kind of bad about this, but it's just under the threshold of
|
||||||
|
# being worth refactoring to be agnostic to interpolation types
|
||||||
|
self.packed_direct_mag = OrderedDict()
|
||||||
self.genome_precalc = OrderedDict()
|
self.genome_precalc = OrderedDict()
|
||||||
self.packed_precalc = OrderedDict()
|
self.packed_precalc = OrderedDict()
|
||||||
self.precalc_code = []
|
self.precalc_code = []
|
||||||
|
|
||||||
self.ns = {}
|
|
||||||
|
|
||||||
self._len = None
|
self._len = None
|
||||||
self.decls = None
|
self.decls = None
|
||||||
self.defs = None
|
self.defs = None
|
||||||
@ -156,29 +143,37 @@ class GenomePacker(object):
|
|||||||
self.search_rounds = util.DEFAULT_SEARCH_ROUNDS
|
self.search_rounds = util.DEFAULT_SEARCH_ROUNDS
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
"""Length in elements. (*4 for length in bytes.)"""
|
||||||
assert self._len is not None, 'len() called before finalize()'
|
assert self._len is not None, 'len() called before finalize()'
|
||||||
return self._len
|
return self._len
|
||||||
|
|
||||||
def view(self, ptr_name, wrapped_obj, prefix):
|
def view(self, val={}):
|
||||||
"""Create a DataPacker view. See DataPackerView class for details."""
|
"""Create a DataPacker view. See DataPackerView class for details."""
|
||||||
self.ns[prefix] = wrapped_obj
|
return PackerWrapper(self, val, self.spec)
|
||||||
return GenomePackerView(self, ptr_name, wrapped_obj, (prefix,))
|
|
||||||
|
|
||||||
def _require(self, name):
|
def _require(self, spec, path):
|
||||||
"""
|
"""
|
||||||
Called to indicate that the named parameter from the original genome
|
Called to indicate that the named parameter from the original genome
|
||||||
must be available during interpolation.
|
must be available during interpolation.
|
||||||
"""
|
"""
|
||||||
self.packed_direct[name] = None
|
if spec.interp == 'mag':
|
||||||
|
self.packed_direct_mag[path] = None
|
||||||
|
else:
|
||||||
|
self.packed_direct[path] = None
|
||||||
|
return self.devname(path)
|
||||||
|
|
||||||
def _require_pre(self, name, mag_scaling=False):
|
def _require_pre(self, spec, path):
|
||||||
i = len(self.genome_precalc) << self.search_rounds
|
i = len(self.genome_precalc) << self.search_rounds
|
||||||
self.genome_precalc[name] = None
|
self.genome_precalc[path] = None
|
||||||
name = 'catmull_rom_mag' if mag_scaling else 'catmull_rom'
|
func = 'catmull_rom_mag' if spec.interp == 'mag' else 'catmull_rom'
|
||||||
return '%s(×[%d], &knots[%d], time)' % (name, i, i)
|
return '%s(×[%d], &knots[%d], time)' % (func, i, i)
|
||||||
|
|
||||||
def _pre_alloc(self, name):
|
def _pre_alloc(self, path):
|
||||||
self.packed_precalc[name] = None
|
self.packed_precalc[path] = None
|
||||||
|
return '%s->%s' % (self.ptr_name, '_'.join(path))
|
||||||
|
|
||||||
|
def devname(self, path):
|
||||||
|
return '%s.%s' % (self.ptr_name, '_'.join(path))
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
"""
|
"""
|
||||||
@ -187,20 +182,18 @@ class GenomePacker(object):
|
|||||||
# At the risk of packing a few things more than once, we don't
|
# At the risk of packing a few things more than once, we don't
|
||||||
# uniquify the overall precalc order, sparing us the need to implement
|
# uniquify the overall precalc order, sparing us the need to implement
|
||||||
# recursive code generation
|
# recursive code generation
|
||||||
self.packed = self.packed_direct.keys() + self.packed_precalc.keys()
|
direct = self.packed_direct.keys() + self.packed_direct_mag.keys()
|
||||||
self.genome = self.packed_direct.keys() + self.genome_precalc.keys()
|
self.packed = direct + self.packed_precalc.keys()
|
||||||
|
self.genome = direct + self.genome_precalc.keys()
|
||||||
|
|
||||||
self._len = len(self.packed)
|
self._len = len(self.packed)
|
||||||
|
|
||||||
decls = self._decls.substitute(packed=self.packed, tname=self.tname)
|
decls = self._decls.substitute(**self.__dict__)
|
||||||
defs = self._defs.substitute(
|
defs = self._defs.substitute(**self.__dict__)
|
||||||
packed_direct=self.packed_direct, tname=self.tname,
|
|
||||||
precalc_code=self.precalc_code,
|
|
||||||
search_rounds=self.search_rounds)
|
|
||||||
|
|
||||||
return devlib(deps=[catmullromlib], decls=decls, defs=defs)
|
return devlib(deps=[catmullromlib], decls=decls, defs=defs)
|
||||||
|
|
||||||
def pack(self, pool=None):
|
def pack(self, gnm, pool=None):
|
||||||
"""
|
"""
|
||||||
Return a packed copy of the genome ready for uploading to the GPU,
|
Return a packed copy of the genome ready for uploading to the GPU,
|
||||||
as two float32 NDArrays for the knot times and values.
|
as two float32 NDArrays for the knot times and values.
|
||||||
@ -213,40 +206,49 @@ class GenomePacker(object):
|
|||||||
times, knots = np.empty((2, len(self.genome), width), 'f4')
|
times, knots = np.empty((2, len(self.genome), width), 'f4')
|
||||||
times.fill(1e9)
|
times.fill(1e9)
|
||||||
|
|
||||||
for idx, gname in enumerate(self.genome):
|
for idx, path in enumerate(self.genome):
|
||||||
attr = self.ns[gname[0]]
|
attr = gnm
|
||||||
for g in gname[1:]:
|
for name in path:
|
||||||
attr = getattr(attr, g)
|
attr = attr[name]
|
||||||
times[idx,:len(attr.knots[0])] = attr.knots[0]
|
attr = SplineEval.normalize(attr)
|
||||||
knots[idx,:len(attr.knots[1])] = attr.knots[1]
|
times[idx,:len(attr[0])] = attr[0]
|
||||||
|
knots[idx,:len(attr[1])] = attr[1]
|
||||||
return times, knots
|
return times, knots
|
||||||
|
|
||||||
_defs = Template(r"""
|
_defs = Template(r"""
|
||||||
__global__ void interp_{{tname}}(
|
__global__ void interp_{{tname}}(
|
||||||
{{tname}}* out,
|
{{tname}}* {{ptr_name}},
|
||||||
const float *times, const float *knots,
|
const float *times, const float *knots,
|
||||||
float tstart, float tstep, int maxid)
|
float tstart, float tstep, int maxid)
|
||||||
{
|
{
|
||||||
int id = gtid();
|
int id = gtid();
|
||||||
if (id >= maxid) return;
|
if (id >= maxid) return;
|
||||||
out = &out[id];
|
{{ptr_name}} = &{{ptr_name}}[id];
|
||||||
float time = tstart + id * tstep;
|
float time = tstart + id * tstep;
|
||||||
|
|
||||||
float *outf = reinterpret_cast<float*>(out);
|
float *outf = reinterpret_cast<float*>({{ptr_name}});
|
||||||
|
|
||||||
|
{{py:lpd = len(packed_direct)}}
|
||||||
|
{{py:lpdm = len(packed_direct_mag)}}
|
||||||
|
|
||||||
// TODO: unroll pragma?
|
// TODO: unroll pragma?
|
||||||
for (int i = 0; i < {{len(packed_direct)}}; i++) {
|
for (int i = 0; i < {{lpd}}; i++) {
|
||||||
int j = i << {{search_rounds}};
|
int j = i << {{search_rounds}};
|
||||||
outf[i] = catmull_rom(×[j], &knots[j], time);
|
outf[i] = catmull_rom(×[j], &knots[j], time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = {{lpd}}; i < {{lpd+lpdm}}; i++) {
|
||||||
|
int j = i << {{search_rounds}};
|
||||||
|
outf[i] = catmull_rom_mag(×[j], &knots[j], time);
|
||||||
|
}
|
||||||
|
|
||||||
// Advance 'times' and 'knots' to the purely generated sections, so that
|
// Advance 'times' and 'knots' to the purely generated sections, so that
|
||||||
// the pregenerated statements emitted by _require_pre are correct.
|
// the pregenerated statements emitted by _require_pre are correct.
|
||||||
times = ×[{{len(packed_direct)<<search_rounds}}];
|
times = ×[{{(lpd+lpdm)<<search_rounds}}];
|
||||||
knots = &knots[{{len(packed_direct)<<search_rounds}}];
|
knots = &knots[{{(lpd+lpdm)<<search_rounds}}];
|
||||||
|
|
||||||
{{for hunk in precalc_code}}
|
{{for hunk in precalc_code}}
|
||||||
if (1) {
|
{
|
||||||
{{hunk}}
|
{{hunk}}
|
||||||
}
|
}
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
@ -255,8 +257,8 @@ __global__ void interp_{{tname}}(
|
|||||||
|
|
||||||
_decls = Template(r"""
|
_decls = Template(r"""
|
||||||
typedef struct {
|
typedef struct {
|
||||||
{{for name in packed}}
|
{{for path in packed}}
|
||||||
float {{'_'.join(name)}};
|
float {{'_'.join(path)}};
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
} {{tname}};
|
} {{tname}};
|
||||||
|
|
||||||
|
@ -7,54 +7,53 @@ import interp
|
|||||||
from util import Template, devlib, ringbuflib
|
from util import Template, devlib, ringbuflib
|
||||||
from mwc import mwclib
|
from mwc import mwclib
|
||||||
|
|
||||||
def precalc_densities(pcp, std_xforms):
|
import cuburn.genome.spec
|
||||||
|
|
||||||
|
def precalc_densities(cp):
|
||||||
# This pattern recurs a few times for precalc segments. Unfortunately,
|
# This pattern recurs a few times for precalc segments. Unfortunately,
|
||||||
# namespace stuff means it's not easy to functionalize this boilerplate
|
# namespace stuff means it's not easy to functionalize this boilerplate
|
||||||
pre_cp = pcp._precalc()
|
cp._code(Template(r"""
|
||||||
pre_cp._code(Template(r"""
|
|
||||||
float sum = 0.0f;
|
float sum = 0.0f;
|
||||||
|
|
||||||
{{for n in std_xforms}}
|
{{for n in cp.xforms}}
|
||||||
float den_{{n}} = {{pre_cp.xforms[n].density}};
|
float den_{{n}} = {{cp.xforms[n].weight}};
|
||||||
sum += den_{{n}};
|
sum += den_{{n}};
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
|
|
||||||
float rsum = 1.0f / sum;
|
float rsum = 1.0f / sum;
|
||||||
sum = 0.0f;
|
sum = 0.0f;
|
||||||
|
|
||||||
{{for n in std_xforms[:-1]}}
|
{{for n in cp.xforms.keys()[:-1]}}
|
||||||
sum += den_{{n}} * rsum;
|
sum += den_{{n}} * rsum;
|
||||||
{{pre_cp._set('den_' + n)}} = sum;
|
{{cp._set('den_' + n)}} = sum;
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
""", name='precalc_densities').substitute(locals()))
|
""", name='precalc_densities').substitute(cp=cp))
|
||||||
|
|
||||||
def precalc_chaos(pcp, std_xforms):
|
def precalc_chaos(cp):
|
||||||
pre_cp = pcp._precalc()
|
cp._code(Template("""
|
||||||
pre_cp._code(Template("""
|
|
||||||
float sum, rsum;
|
float sum, rsum;
|
||||||
|
|
||||||
{{for p in std_xforms}}
|
{{for p in cp.xforms}}
|
||||||
sum = 0.0f;
|
sum = 0.0f;
|
||||||
|
|
||||||
{{for n in std_xforms}}
|
{{for n in cp.xforms}}
|
||||||
float den_{{p}}_{{n}} = {{pre_cp.xforms[p].chaos[n]}};
|
float den_{{p}}_{{n}} = {{cp.xforms[n].weight}}
|
||||||
|
* {{cp.xforms[p].chaos[n]}};
|
||||||
sum += den_{{p}}_{{n}};
|
sum += den_{{p}}_{{n}};
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
|
|
||||||
rsum = 1.0f / sum;
|
rsum = 1.0f / sum;
|
||||||
sum = 0.0f;
|
sum = 0.0f;
|
||||||
|
|
||||||
{{for n in std_xforms[:-1]}}
|
{{for n in cp.xforms.keys()[:-1]}}
|
||||||
sum += den_{{p}}_{{n}} * rsum;
|
sum += den_{{p}}_{{n}} * rsum;
|
||||||
{{pre_cp._set('chaos_%s_%s' % (p, n))}} = sum;
|
{{cp._set('chaos_%s_%s' % (p, n))}} = sum;
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
|
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
""", name='precalc_chaos').substitute(locals()))
|
""", name='precalc_chaos').substitute(cp=cp))
|
||||||
|
|
||||||
def precalc_camera(pcam):
|
|
||||||
pre_cam = pcam._precalc()
|
|
||||||
|
|
||||||
|
def precalc_camera(cam):
|
||||||
# Maxima code to check my logic:
|
# Maxima code to check my logic:
|
||||||
# matrix([1,0,0.5*width + g],[0,1,0.5*height+g],[0,0,1])
|
# matrix([1,0,0.5*width + g],[0,1,0.5*height+g],[0,0,1])
|
||||||
# . matrix([width * scale,0,0], [0,width * scale,0], [0,0,1])
|
# . matrix([width * scale,0,0], [0,width * scale,0], [0,0,1])
|
||||||
@ -62,41 +61,41 @@ def precalc_camera(pcam):
|
|||||||
# . matrix([1,0,-cenx],[0,1,-ceny],[0,0,1])
|
# . matrix([1,0,-cenx],[0,1,-ceny],[0,0,1])
|
||||||
# . matrix([X],[Y],[1]);
|
# . matrix([X],[Y],[1]);
|
||||||
|
|
||||||
pre_cam._code(Template(r"""
|
cam._code(Template(r"""
|
||||||
float rot = {{pre_cam.rotation}} * M_PI / 180.0f;
|
float rot = {{cam.rotation}} * M_PI / 180.0f;
|
||||||
float rotsin = sin(rot), rotcos = cos(rot);
|
float rotsin = sin(rot), rotcos = cos(rot);
|
||||||
float cenx = {{pre_cam.center.x}}, ceny = {{pre_cam.center.y}};
|
float cenx = {{cam.center.x}}, ceny = {{cam.center.y}};
|
||||||
float scale = {{pre_cam.scale}} * acc_size.width;
|
float scale = {{cam.scale}} * acc_size.width;
|
||||||
|
|
||||||
{{pre_cam._set('xx')}} = scale * rotcos;
|
{{cam._set('xx')}} = scale * rotcos;
|
||||||
{{pre_cam._set('xy')}} = scale * -rotsin;
|
{{cam._set('xy')}} = scale * -rotsin;
|
||||||
{{pre_cam._set('xo')}} = scale * (rotsin * ceny - rotcos * cenx)
|
{{cam._set('xo')}} = scale * (rotsin * ceny - rotcos * cenx)
|
||||||
+ 0.5f * acc_size.awidth;
|
+ 0.5f * acc_size.awidth;
|
||||||
|
|
||||||
{{pre_cam._set('yx')}} = scale * rotsin;
|
{{cam._set('yx')}} = scale * rotsin;
|
||||||
{{pre_cam._set('yy')}} = scale * rotcos;
|
{{cam._set('yy')}} = scale * rotcos;
|
||||||
{{pre_cam._set('yo')}} = scale * -(rotsin * cenx + rotcos * ceny)
|
{{cam._set('yo')}} = scale * -(rotsin * cenx + rotcos * ceny)
|
||||||
+ 0.5f * acc_size.aheight;
|
+ 0.5f * acc_size.aheight;
|
||||||
""", 'precalc_camera').substitute(locals()))
|
""", 'precalc_camera').substitute(cam=cam))
|
||||||
|
|
||||||
def precalc_xf_affine(px):
|
def precalc_xf_affine(px):
|
||||||
pre = px._precalc()
|
px._code(Template(r"""
|
||||||
pre._code(Template(r"""
|
float pri = {{px.angle}} * M_PI / 180.0f;
|
||||||
float pri = {{pre.angle}} * M_PI / 180.0f;
|
float spr = {{px.spread}} * M_PI / 180.0f;
|
||||||
float spr = {{pre.spread}} * M_PI / 180.0f;
|
|
||||||
|
|
||||||
float magx = {{pre.magnitude.x._magscale()}};
|
float magx = {{px.magnitude.x}};
|
||||||
float magy = {{pre.magnitude.y._magscale()}};
|
float magy = {{px.magnitude.y}};
|
||||||
|
|
||||||
{{pre._set('xx')}} = magx * cos(pri-spr);
|
{{px._set('xx')}} = magx * cos(pri-spr);
|
||||||
{{pre._set('yx')}} = -magx * sin(pri-spr);
|
{{px._set('yx')}} = -magx * sin(pri-spr);
|
||||||
{{pre._set('xy')}} = -magy * cos(pri+spr);
|
{{px._set('xy')}} = -magy * cos(pri+spr);
|
||||||
{{pre._set('yy')}} = magy * sin(pri+spr);
|
{{px._set('yy')}} = magy * sin(pri+spr);
|
||||||
{{pre._set('xo')}} = {{pre.offset.x._magscale()}};
|
{{px._set('xo')}} = {{px.offset.x}};
|
||||||
{{pre._set('yo')}} = -{{pre.offset.y._magscale()}};
|
{{px._set('yo')}} = -{{px.offset.y}};
|
||||||
""", 'precalc_xf_affine').substitute(locals()))
|
""", 'precalc_xf_affine').substitute(px=px))
|
||||||
|
|
||||||
def apply_affine(x, y, xo, yo, packer):
|
def apply_affine(names, packer):
|
||||||
|
x, y, xo, yo = names.split()
|
||||||
return Template("""
|
return Template("""
|
||||||
{{xo}} = {{packer.xx}} * {{x}} + {{packer.xy}} * {{y}} + {{packer.xo}};
|
{{xo}} = {{packer.xx}} * {{x}} + {{packer.xy}} * {{y}} + {{packer.xo}};
|
||||||
{{yo}} = {{packer.yx}} * {{x}} + {{packer.yy}} * {{y}} + {{packer.yo}};
|
{{yo}} = {{packer.yx}} * {{x}} + {{packer.yy}} * {{y}} + {{packer.yo}};
|
||||||
@ -126,25 +125,24 @@ __device__
|
|||||||
void apply_xf_{{xfid}}(float &ox, float &oy, float &color, mwc_st &rctx) {
|
void apply_xf_{{xfid}}(float &ox, float &oy, float &color, mwc_st &rctx) {
|
||||||
float tx, ty;
|
float tx, ty;
|
||||||
|
|
||||||
{{precalc_xf_affine(px.affine)}}
|
{{precalc_xf_affine(px.pre_affine._precalc())}}
|
||||||
{{apply_affine('ox', 'oy', 'tx', 'ty', px.affine)}}
|
{{apply_affine('ox oy tx ty', px.pre_affine)}}
|
||||||
|
|
||||||
ox = 0;
|
ox = 0;
|
||||||
oy = 0;
|
oy = 0;
|
||||||
|
|
||||||
{{for name in xform.variations}}
|
{{for name, pv in px.variations.items()}}
|
||||||
if (1) {
|
{
|
||||||
{{py:pv = px.variations[name]}}
|
|
||||||
float w = {{pv.weight}};
|
float w = {{pv.weight}};
|
||||||
{{variations.var_code[name].substitute(locals())}}
|
{{variations.var_code[name].substitute(locals())}}
|
||||||
}
|
}
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
|
|
||||||
{{if 'post' in xform}}
|
{{if 'post_affine' in px}}
|
||||||
tx = ox;
|
tx = ox;
|
||||||
ty = oy;
|
ty = oy;
|
||||||
{{precalc_xf_affine(px.post)}}
|
{{precalc_xf_affine(px.post_affine._precalc())}}
|
||||||
{{apply_affine('tx', 'ty', 'ox', 'oy', px.post)}}
|
{{apply_affine('tx ty ox oy', px.post_affine)}}
|
||||||
{{endif}}
|
{{endif}}
|
||||||
|
|
||||||
float csp = {{px.color_speed}};
|
float csp = {{px.color_speed}};
|
||||||
@ -152,10 +150,8 @@ void apply_xf_{{xfid}}(float &ox, float &oy, float &color, mwc_st &rctx) {
|
|||||||
};
|
};
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def iter_xf_body(pcp, xfid, xform):
|
def iter_xf_body(cp, xfid, px):
|
||||||
px = pcp.xforms[xfid]
|
|
||||||
tmpl = Template(iter_xf_body_code, 'apply_xf_'+xfid)
|
tmpl = Template(iter_xf_body_code, 'apply_xf_'+xfid)
|
||||||
|
|
||||||
g = dict(globals())
|
g = dict(globals())
|
||||||
g.update(locals())
|
g.update(locals())
|
||||||
return tmpl.substitute(g)
|
return tmpl.substitute(g)
|
||||||
@ -176,13 +172,16 @@ iter(uint64_t out_ptr, uint64_t atom_ptr,
|
|||||||
int this_rb_idx = rb_incr(rb->head, blockDim.x * threadIdx.y + threadIdx.x);
|
int this_rb_idx = rb_incr(rb->head, blockDim.x * threadIdx.y + threadIdx.x);
|
||||||
mwc_st rctx = msts[this_rb_idx];
|
mwc_st rctx = msts[this_rb_idx];
|
||||||
|
|
||||||
{{precalc_camera(pcp.camera)}}
|
{{precalc_camera(cp.camera._precalc())}}
|
||||||
if (threadIdx.y == 5 && threadIdx.x == 4) {
|
if (threadIdx.y == 5 && threadIdx.x == 4) {
|
||||||
float ditherwidth = {{pcp.camera.dither_width}} * 0.5f;
|
if (blockIdx.x == 0)
|
||||||
{{pcp.camera.xo}} += ditherwidth * mwc_next_11(rctx);
|
printf("Hiya %f\n", {{cp.camera.xx}});
|
||||||
{{pcp.camera.yo}} += ditherwidth * mwc_next_11(rctx);
|
float ditherwidth = {{cp.camera.dither_width}} * 0.5f;
|
||||||
|
{{cp.camera.xo}} += ditherwidth * mwc_next_11(rctx);
|
||||||
|
{{cp.camera.yo}} += ditherwidth * mwc_next_11(rctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: spare the register, reuse at call site?
|
// TODO: spare the register, reuse at call site?
|
||||||
int time = blockIdx.x >> 4;
|
int time = blockIdx.x >> 4;
|
||||||
float color_dither = 0.49f * mwc_next_11(rctx);
|
float color_dither = 0.49f * mwc_next_11(rctx);
|
||||||
@ -229,24 +228,26 @@ iter(uint64_t out_ptr, uint64_t atom_ptr,
|
|||||||
color = mwc_next_01(rctx);
|
color = mwc_next_01(rctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{{py:xk = cp.xforms.keys()}}
|
||||||
{{if chaos_used}}
|
{{if chaos_used}}
|
||||||
|
|
||||||
{{precalc_chaos(pcp, std_xforms)}}
|
{{precalc_chaos(cp)}}
|
||||||
|
|
||||||
// For now, we don't attempt to use the swap buffer when chaos is used
|
// For now, we don't attempt to use the swap buffer when chaos is used
|
||||||
float xfsel = mwc_next_01(rctx);
|
float xfsel = mwc_next_01(rctx);
|
||||||
|
|
||||||
{{for prior_xform_idx, prior_xform_name in enumerate(std_xforms)}}
|
{{for prior_xform_idx, prior_xform_name in enumerate(xk)}}
|
||||||
if (last_xf_used == {{prior_xform_idx}}) {
|
if (last_xf_used == {{prior_xform_idx}}) {
|
||||||
{{for xform_idx, xform_name in enumerate(std_xforms[:-1])}}
|
{{for xform_idx, xform_name in enumerate(xk[:-1])}}
|
||||||
if (xfsel <= {{pcp['chaos_'+prior_xform_name+'_'+xform_name]}}) {
|
if (xfsel <= {{cp['chaos_'+prior_xform_name+'_'+xform_name]}}) {
|
||||||
apply_xf_{{xform_name}}(x, y, color, rctx);
|
apply_xf_{{xform_name}}(x, y, color, rctx);
|
||||||
last_xf_used = {{xform_idx}};
|
last_xf_used = {{xform_idx}};
|
||||||
} else
|
} else
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
{
|
{
|
||||||
apply_xf_{{std_xforms[-1]}}(x, y, color, rctx);
|
apply_xf_{{xk[-1]}}(x, y, color, rctx);
|
||||||
last_xf_used = {{len(std_xforms)-1}};
|
last_xf_used = {{len(xk)-1}};
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
@ -256,18 +257,18 @@ iter(uint64_t out_ptr, uint64_t atom_ptr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
{{else}}
|
{{else}}
|
||||||
{{precalc_densities(pcp, std_xforms)}}
|
{{precalc_densities(cp._precalc())}}
|
||||||
float xfsel = cosel[threadIdx.y];
|
float xfsel = cosel[threadIdx.y];
|
||||||
|
|
||||||
{{for xform_idx, xform_name in enumerate(std_xforms[:-1])}}
|
{{for xform_idx, xform_name in enumerate(xk[:-1])}}
|
||||||
if (xfsel <= {{pcp['den_'+xform_name]}}) {
|
if (xfsel <= {{cp['den_'+xform_name]}}) {
|
||||||
apply_xf_{{xform_name}}(x, y, color, rctx);
|
apply_xf_{{xform_name}}(x, y, color, rctx);
|
||||||
last_xf_used = {{xform_idx}};
|
last_xf_used = {{xform_idx}};
|
||||||
} else
|
} else
|
||||||
{{endfor}}
|
{{endfor}}
|
||||||
{
|
{
|
||||||
apply_xf_{{std_xforms[-1]}}(x, y, color, rctx);
|
apply_xf_{{xk[-1]}}(x, y, color, rctx);
|
||||||
last_xf_used = {{len(std_xforms)-1}};
|
last_xf_used = {{len(xk)-1}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate points between threads.
|
// Rotate points between threads.
|
||||||
@ -298,18 +299,14 @@ iter(uint64_t out_ptr, uint64_t atom_ptr,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
{{if 'final' in cp.xforms}}
|
float cx, cy, cc;
|
||||||
|
{{if 'final_xform' in cp}}
|
||||||
float fx = x, fy = y, fcolor = color;
|
float fx = x, fy = y, fcolor = color;
|
||||||
apply_xf_final(fx, fy, fcolor, rctx);
|
apply_xf_final(fx, fy, fcolor, rctx);
|
||||||
{{endif}}
|
{{apply_affine('fx fy cx cy', cp.camera)}}
|
||||||
|
|
||||||
float cx, cy, cc;
|
|
||||||
|
|
||||||
{{if 'final' in cp.xforms}}
|
|
||||||
{{apply_affine('fx', 'fy', 'cx', 'cy', pcp.camera)}}
|
|
||||||
cc = fcolor;
|
cc = fcolor;
|
||||||
{{else}}
|
{{else}}
|
||||||
{{apply_affine('x', 'y', 'cx', 'cy', pcp.camera)}}
|
{{apply_affine('x y cx cy', cp.camera)}}
|
||||||
cc = color;
|
cc = color;
|
||||||
{{endif}}
|
{{endif}}
|
||||||
|
|
||||||
@ -407,11 +404,9 @@ oflow_end:
|
|||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def iter_body(cp, pcp):
|
def iter_body(cp):
|
||||||
# For legacy reasons, 'cp' is used here instead of 'genome'.
|
|
||||||
tmpl = Template(iter_body_code, 'iter_body')
|
tmpl = Template(iter_body_code, 'iter_body')
|
||||||
NWARPS = NTHREADS / 32
|
NWARPS = NTHREADS / 32
|
||||||
std_xforms = [n for n in sorted(cp.xforms) if n != 'final']
|
|
||||||
|
|
||||||
# TODO: detect this properly and use it
|
# TODO: detect this properly and use it
|
||||||
chaos_used = False
|
chaos_used = False
|
||||||
@ -420,12 +415,15 @@ def iter_body(cp, pcp):
|
|||||||
vars.update(locals())
|
vars.update(locals())
|
||||||
return tmpl.substitute(vars)
|
return tmpl.substitute(vars)
|
||||||
|
|
||||||
def mkiterlib(genome):
|
def mkiterlib(gnm):
|
||||||
packer = interp.GenomePacker('iter_params')
|
packer = interp.GenomePacker('iter_params', 'params',
|
||||||
pcp = packer.view('params', genome, 'cp')
|
cuburn.genome.spec.anim)
|
||||||
|
cp = packer.view(gnm)
|
||||||
|
|
||||||
iterbody = iter_body(genome, pcp)
|
iterbody = iter_body(cp)
|
||||||
bodies = [iter_xf_body(pcp, i, x) for i, x in sorted(genome.xforms.items())]
|
bodies = [iter_xf_body(cp, i, x) for i, x in sorted(cp.xforms.items())]
|
||||||
|
if 'final_xform' in cp:
|
||||||
|
bodies.append(iter_xf_body(cp, 'final', cp.final_xform))
|
||||||
bodies.append(iterbody)
|
bodies.append(iterbody)
|
||||||
packer_lib = packer.finalize()
|
packer_lib = packer.finalize()
|
||||||
|
|
||||||
|
@ -3,53 +3,39 @@ import numpy as np
|
|||||||
from util import Template
|
from util import Template
|
||||||
|
|
||||||
var_code = {}
|
var_code = {}
|
||||||
var_params = {}
|
|
||||||
|
|
||||||
def var(num, name, code, params=None):
|
def var(name, code, precalc=None):
|
||||||
var_code[name] = Template(code, name)
|
precalc_fun = None
|
||||||
if params is not None:
|
if precalc:
|
||||||
r = {}
|
def precalc_fun(pv, px):
|
||||||
for p in params.split():
|
pv, px = pv._precalc(), px._precalc()
|
||||||
if '=' in p:
|
tmpl = Template(precalc, name+'_precalc').substitute(pv=pv, px=px)
|
||||||
p, default = p.split('=')
|
pv._code(tmpl)
|
||||||
if default == 'M_PI':
|
code = "\n {{precalc_fun(pv, px)}}" + code
|
||||||
default = np.pi
|
var_code[name] = Template(code, name,
|
||||||
else:
|
namespace=dict(precalc_fun=precalc_fun))
|
||||||
default = float(default)
|
|
||||||
else:
|
|
||||||
default = 0.0
|
|
||||||
r[p] = default
|
|
||||||
var_params[name] = r
|
|
||||||
|
|
||||||
# TODO: This is a shitty hack
|
|
||||||
def precalc(name, code):
|
|
||||||
def precalc_fun(pv, px=None):
|
|
||||||
pre = pv._precalc()
|
|
||||||
prex = px._precalc() if px is not None else None
|
|
||||||
pre._code(Template(code, name+'_precalc').substitute(**locals()))
|
|
||||||
Template.default_namespace[name+'_precalc'] = precalc_fun
|
|
||||||
|
|
||||||
# Variables note: all functions will have their weights as 'w',
|
# Variables note: all functions will have their weights as 'w',
|
||||||
# input variables 'tx' and 'ty', and output 'ox' and 'oy' available
|
# input variables 'tx' and 'ty', and output 'ox' and 'oy' available
|
||||||
# from the calling context. Each statement will be placed inside brackets,
|
# from the calling context. Each statement will be placed inside brackets,
|
||||||
# to avoid namespace pollution.
|
# to avoid namespace pollution.
|
||||||
var(0, 'linear', """
|
var('linear', """
|
||||||
ox += tx * w;
|
ox += tx * w;
|
||||||
oy += ty * w;
|
oy += ty * w;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(1, 'sinusoidal', """
|
var('sinusoidal', """
|
||||||
ox += w * sinf(tx);
|
ox += w * sinf(tx);
|
||||||
oy += w * sinf(ty);
|
oy += w * sinf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(2, 'spherical', """
|
var('spherical', """
|
||||||
float r2 = w / (tx*tx + ty*ty);
|
float r2 = w / (tx*tx + ty*ty);
|
||||||
ox += tx * r2;
|
ox += tx * r2;
|
||||||
oy += ty * r2;
|
oy += ty * r2;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(3, 'swirl', """
|
var('swirl', """
|
||||||
float r2 = tx*tx + ty*ty;
|
float r2 = tx*tx + ty*ty;
|
||||||
float c1 = sinf(r2);
|
float c1 = sinf(r2);
|
||||||
float c2 = cosf(r2);
|
float c2 = cosf(r2);
|
||||||
@ -57,25 +43,25 @@ var(3, 'swirl', """
|
|||||||
oy += w * (c2*tx + c1*ty);
|
oy += w * (c2*tx + c1*ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(4, 'horseshoe', """
|
var('horseshoe', """
|
||||||
float r = w / sqrtf(tx*tx + ty*ty);
|
float r = w / sqrtf(tx*tx + ty*ty);
|
||||||
ox += r * (tx - ty) * (tx + ty);
|
ox += r * (tx - ty) * (tx + ty);
|
||||||
oy += 2.0f * tx * ty * r;
|
oy += 2.0f * tx * ty * r;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(5, 'polar', """
|
var('polar', """
|
||||||
ox += w * atan2f(tx, ty) * M_1_PI;
|
ox += w * atan2f(tx, ty) * M_1_PI;
|
||||||
oy += w * (sqrtf(tx * tx + ty * ty) - 1.0f);
|
oy += w * (sqrtf(tx * tx + ty * ty) - 1.0f);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(6, 'handkerchief', """
|
var('handkerchief', """
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
ox += w * r * sinf(a+r);
|
ox += w * r * sinf(a+r);
|
||||||
oy += w * r * cosf(a-r);
|
oy += w * r * cosf(a-r);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(7, 'heart', """
|
var('heart', """
|
||||||
float sq = sqrtf(tx*tx + ty*ty);
|
float sq = sqrtf(tx*tx + ty*ty);
|
||||||
float a = sq * atan2f(tx, ty);
|
float a = sq * atan2f(tx, ty);
|
||||||
float r = w * sq;
|
float r = w * sq;
|
||||||
@ -83,14 +69,14 @@ var(7, 'heart', """
|
|||||||
oy -= r * cosf(a);
|
oy -= r * cosf(a);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(8, 'disc', """
|
var('disc', """
|
||||||
float a = w * atan2f(tx, ty) * M_1_PI;
|
float a = w * atan2f(tx, ty) * M_1_PI;
|
||||||
float r = M_PI * sqrtf(tx*tx + ty*ty);
|
float r = M_PI * sqrtf(tx*tx + ty*ty);
|
||||||
ox += sinf(r) * a;
|
ox += sinf(r) * a;
|
||||||
oy += cosf(r) * a;
|
oy += cosf(r) * a;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(9, 'spiral', """
|
var('spiral', """
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
float r1 = w / r;
|
float r1 = w / r;
|
||||||
@ -98,21 +84,21 @@ var(9, 'spiral', """
|
|||||||
oy += r1 * (sinf(a) - cosf(r));
|
oy += r1 * (sinf(a) - cosf(r));
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(10, 'hyperbolic', """
|
var('hyperbolic', """
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
ox += w * sinf(a) / r;
|
ox += w * sinf(a) / r;
|
||||||
oy += w * cosf(a) * r;
|
oy += w * cosf(a) * r;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(11, 'diamond', """
|
var('diamond', """
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
ox += w * sinf(a) * cosf(r);
|
ox += w * sinf(a) * cosf(r);
|
||||||
oy += w * cosf(a) * sinf(r);
|
oy += w * cosf(a) * sinf(r);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(12, 'ex', """
|
var('ex', """
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
float n0 = sinf(a+r);
|
float n0 = sinf(a+r);
|
||||||
@ -123,7 +109,7 @@ var(12, 'ex', """
|
|||||||
oy += w * (m0 - m1);
|
oy += w * (m0 - m1);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(13, 'julia', """
|
var('julia', """
|
||||||
float a = 0.5f * atan2f(tx, ty);
|
float a = 0.5f * atan2f(tx, ty);
|
||||||
if (mwc_next(rctx) & 1) a += M_PI;
|
if (mwc_next(rctx) & 1) a += M_PI;
|
||||||
float r = w * sqrtf(sqrtf(tx*tx + ty*ty)); // TODO: fastest?
|
float r = w * sqrtf(sqrtf(tx*tx + ty*ty)); // TODO: fastest?
|
||||||
@ -131,7 +117,7 @@ var(13, 'julia', """
|
|||||||
oy += r * sinf(a);
|
oy += r * sinf(a);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(14, 'bent', """
|
var('bent', """
|
||||||
float nx = 1.0f;
|
float nx = 1.0f;
|
||||||
if (tx < 0.0f) nx = 2.0f;
|
if (tx < 0.0f) nx = 2.0f;
|
||||||
float ny = 1.0f;
|
float ny = 1.0f;
|
||||||
@ -140,37 +126,34 @@ var(14, 'bent', """
|
|||||||
oy += w * ny * ty;
|
oy += w * ny * ty;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
precalc('waves', """
|
var('waves', """
|
||||||
float dx = {{prex.affine.offset.x}};
|
float c10 = {{px.pre_affine.xy}};
|
||||||
float dy = {{prex.affine.offset.y}};
|
float c11 = {{px.pre_affine.yy}};
|
||||||
{{pre._set('dx2')}} = 1.0f / (dx * dx + 1.0e-20f);
|
|
||||||
{{pre._set('dy2')}} = 1.0f / (dy * dy + 1.0e-20f);
|
|
||||||
""")
|
|
||||||
|
|
||||||
var(15, 'waves', """
|
|
||||||
{{waves_precalc(pv, px)}}
|
|
||||||
float c10 = {{px.affine.xy}};
|
|
||||||
float c11 = {{px.affine.yy}};
|
|
||||||
|
|
||||||
ox += w * (tx + c10 * sinf(ty * {{pv.dx2}}));
|
ox += w * (tx + c10 * sinf(ty * {{pv.dx2}}));
|
||||||
oy += w * (ty + c11 * sinf(tx * {{pv.dy2}}));
|
oy += w * (ty + c11 * sinf(tx * {{pv.dy2}}));
|
||||||
|
""", """
|
||||||
|
float dx = {{px.pre_affine.offset.x}};
|
||||||
|
float dy = {{px.pre_affine.offset.y}};
|
||||||
|
{{pv._set('dx2')}} = 1.0f / (dx * dx + 1.0e-20f);
|
||||||
|
{{pv._set('dy2')}} = 1.0f / (dy * dy + 1.0e-20f);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(16, 'fisheye', """
|
var('fisheye', """
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
r = 2.0f * w / (r + 1.0f);
|
r = 2.0f * w / (r + 1.0f);
|
||||||
ox += r * ty;
|
ox += r * ty;
|
||||||
oy += r * tx;
|
oy += r * tx;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(17, 'popcorn', """
|
var('popcorn', """
|
||||||
float dx = tanf(3.0f*ty);
|
float dx = tanf(3.0f*ty);
|
||||||
float dy = tanf(3.0f*tx);
|
float dy = tanf(3.0f*tx);
|
||||||
ox += w * (tx + {{px.affine.xo}} * sinf(dx));
|
ox += w * (tx + {{px.pre_affine.xo}} * sinf(dx));
|
||||||
oy += w * (ty + {{px.affine.yo}} * sinf(dy));
|
oy += w * (ty + {{px.pre_affine.yo}} * sinf(dy));
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(18, 'exponential', """
|
var('exponential', """
|
||||||
float dx = w * expf(tx - 1.0f);
|
float dx = w * expf(tx - 1.0f);
|
||||||
if (isfinite(dx)) {
|
if (isfinite(dx)) {
|
||||||
float dy = M_PI * ty;
|
float dy = M_PI * ty;
|
||||||
@ -179,7 +162,7 @@ var(18, 'exponential', """
|
|||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(19, 'power', """
|
var('power', """
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
float sa = sinf(a);
|
float sa = sinf(a);
|
||||||
float r = w * powf(sqrtf(tx*tx + ty*ty),sa);
|
float r = w * powf(sqrtf(tx*tx + ty*ty),sa);
|
||||||
@ -187,14 +170,15 @@ var(19, 'power', """
|
|||||||
oy += r * sa;
|
oy += r * sa;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(20, 'cosine', """
|
var('cosine', """
|
||||||
float a = M_PI * tx;
|
float a = M_PI * tx;
|
||||||
ox += w * cosf(a) * coshf(ty);
|
ox += w * cosf(a) * coshf(ty);
|
||||||
oy -= w * sinf(a) * sinhf(ty);
|
oy -= w * sinf(a) * sinhf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(21, 'rings', """
|
var('rings', """
|
||||||
float dx = {{px.affine.xo}} * {{px.affine.xo}};
|
float dx = {{px.pre_affine.xo}};
|
||||||
|
dx *= dx;
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
r = w * (fmodf(r+dx, 2.0f*dx) - dx + r * (1.0f - dx));
|
r = w * (fmodf(r+dx, 2.0f*dx) - dx + r * (1.0f - dx));
|
||||||
@ -202,10 +186,11 @@ var(21, 'rings', """
|
|||||||
oy += r * sinf(a);
|
oy += r * sinf(a);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(22, 'fan', """
|
var('fan', """
|
||||||
float dx = M_PI * ({{px.affine.xo}} * {{px.affine.xo}});
|
float dx = {{px.pre_affine.xo}};
|
||||||
|
dx *= dx * M_PI;
|
||||||
float dx2 = 0.5f * dx;
|
float dx2 = 0.5f * dx;
|
||||||
float dy = {{px.affine.yo}};
|
float dy = {{px.pre_affine.yo}};
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
a += (fmodf(a+dy, dx) > dx2) ? -dx2 : dx2;
|
a += (fmodf(a+dy, dx) > dx2) ? -dx2 : dx2;
|
||||||
float r = w * sqrtf(tx*tx + ty*ty);
|
float r = w * sqrtf(tx*tx + ty*ty);
|
||||||
@ -213,27 +198,28 @@ var(22, 'fan', """
|
|||||||
oy += r * sinf(a);
|
oy += r * sinf(a);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(23, 'blob', """
|
var('blob', """
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
float bdiff = 0.5f * ({{pv.high}} - {{pv.low}});
|
float bdiff = 0.5f * ({{pv.high}} - {{pv.low}});
|
||||||
r *= w * ({{pv.low}} + bdiff * (1.0f + sinf({{pv.waves}} * a)));
|
r *= w * ({{pv.low}} + bdiff * (1.0f + sinf({{pv.waves}} * a)));
|
||||||
ox += sinf(a) * r;
|
ox += sinf(a) * r;
|
||||||
oy += cosf(a) * r;
|
oy += cosf(a) * r;
|
||||||
""", 'low high=1 waves=1')
|
""")
|
||||||
|
|
||||||
var(24, 'pdj', """
|
var('pdj', """
|
||||||
float nx1 = cosf({{pv.b}} * tx);
|
float nx1 = cosf({{pv.b}} * tx);
|
||||||
float nx2 = sinf({{pv.c}} * tx);
|
float nx2 = sinf({{pv.c}} * tx);
|
||||||
float ny1 = sinf({{pv.a}} * ty);
|
float ny1 = sinf({{pv.a}} * ty);
|
||||||
float ny2 = cosf({{pv.d}} * ty);
|
float ny2 = cosf({{pv.d}} * ty);
|
||||||
ox += w * (ny1 - nx1);
|
ox += w * (ny1 - nx1);
|
||||||
oy += w * (nx2 - ny2);
|
oy += w * (nx2 - ny2);
|
||||||
""", 'a b c d')
|
""")
|
||||||
|
|
||||||
var(25, 'fan2', """
|
var('fan2', """
|
||||||
float dy = {{pv.y}};
|
float dy = {{pv.y}};
|
||||||
float dx = M_PI * {{pv.x}} * {{pv.x}};
|
float dx = {{pv.x}};
|
||||||
|
dx *= dx * M_PI;
|
||||||
float dx2 = 0.5f * dx;
|
float dx2 = 0.5f * dx;
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
float r = w * sqrtf(tx*tx + ty*ty);
|
float r = w * sqrtf(tx*tx + ty*ty);
|
||||||
@ -245,62 +231,55 @@ var(25, 'fan2', """
|
|||||||
|
|
||||||
ox += r * sinf(a);
|
ox += r * sinf(a);
|
||||||
oy += r * cosf(a);
|
oy += r * cosf(a);
|
||||||
""", 'x y')
|
""")
|
||||||
|
|
||||||
var(26, 'rings2', """
|
var('rings2', """
|
||||||
float dx = {{pv.val}} * {{pv.val}};
|
float dx = {{pv.val}};
|
||||||
|
dx *= dx;
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
float a = atan2f(tx, ty);
|
float a = atan2f(tx, ty);
|
||||||
r += -2.0f * dx * (int)((r+dx)/(2.0f*dx)) + r * (1.0f - dx);
|
r += -2.0f * dx * (int)((r+dx)/(2.0f*dx)) + r * (1.0f - dx);
|
||||||
ox += w * sinf(a) * r;
|
ox += w * sinf(a) * r;
|
||||||
oy += w * cosf(a) * r;
|
oy += w * cosf(a) * r;
|
||||||
""", 'val')
|
""")
|
||||||
|
|
||||||
var(27, 'eyefish', """
|
var('eyefish', """
|
||||||
float r = 2.0f * w / (sqrtf(tx*tx + ty*ty) + 1.0f);
|
float r = 2.0f * w / (sqrtf(tx*tx + ty*ty) + 1.0f);
|
||||||
ox += r * tx;
|
ox += r * tx;
|
||||||
oy += r * ty;
|
oy += r * ty;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(28, 'bubble', """
|
var('bubble', """
|
||||||
float r = w / (0.25f * (tx*tx + ty*ty) + 1.0f);
|
float r = w / (0.25f * (tx*tx + ty*ty) + 1.0f);
|
||||||
ox += r * tx;
|
ox += r * tx;
|
||||||
oy += r * ty;
|
oy += r * ty;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(29, 'cylinder', """
|
var('cylinder', """
|
||||||
ox += w * sinf(tx);
|
ox += w * sinf(tx);
|
||||||
oy += w * ty;
|
oy += w * ty;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
precalc('perspective', """
|
var('perspective', """
|
||||||
float pang = {{pre.angle}} * M_PI_2;
|
|
||||||
float pdist = fmaxf(1e-9, {{pre.dist}});
|
|
||||||
{{pre._set('mdist')}} = pdist;
|
|
||||||
{{pre._set('sin')}} = sin(pang);
|
|
||||||
{{pre._set('cos')}} = pdist * cos(pang);
|
|
||||||
""")
|
|
||||||
|
|
||||||
var(30, 'perspective', """
|
|
||||||
{{perspective_precalc(pv)}}
|
|
||||||
|
|
||||||
float t = 1.0f / ({{pv.mdist}} - ty * {{pv.sin}});
|
float t = 1.0f / ({{pv.mdist}} - ty * {{pv.sin}});
|
||||||
ox += w * {{pv.mdist}} * tx * t;
|
ox += w * {{pv.mdist}} * tx * t;
|
||||||
oy += w * {{pv.cos}} * ty * t;
|
oy += w * {{pv.cos}} * ty * t;
|
||||||
""", 'angle dist')
|
""", """
|
||||||
|
float pang = {{pv.angle}} * M_PI_2;
|
||||||
|
float pdist = fmaxf(1e-9, {{pv.dist}});
|
||||||
|
{{pv._set('mdist')}} = pdist;
|
||||||
|
{{pv._set('sin')}} = sin(pang);
|
||||||
|
{{pv._set('cos')}} = pdist * cos(pang);
|
||||||
|
""")
|
||||||
|
|
||||||
var(31, 'noise', """
|
var('noise', """
|
||||||
float tmpr = mwc_next_01(rctx) * 2.0f * M_PI;
|
float tmpr = mwc_next_01(rctx) * 2.0f * M_PI;
|
||||||
float r = w * mwc_next_01(rctx);
|
float r = w * mwc_next_01(rctx);
|
||||||
ox += tx * r * cosf(tmpr);
|
ox += tx * r * cosf(tmpr);
|
||||||
oy += ty * r * sinf(tmpr);
|
oy += ty * r * sinf(tmpr);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
precalc('julian',
|
var('julian', """
|
||||||
"{{pre._set('cn')}} = {{pre.dist}} / (2.0f * {{pre.power}});\n")
|
|
||||||
|
|
||||||
var(32, 'julian', """
|
|
||||||
{{julian_precalc(pv)}}
|
|
||||||
float power = {{pv.power}};
|
float power = {{pv.power}};
|
||||||
float t_rnd = truncf(mwc_next_01(rctx) * fabsf(power));
|
float t_rnd = truncf(mwc_next_01(rctx) * fabsf(power));
|
||||||
float a = atan2f(ty, tx);
|
float a = atan2f(ty, tx);
|
||||||
@ -310,14 +289,11 @@ var(32, 'julian', """
|
|||||||
|
|
||||||
ox += r * cosf(tmpr);
|
ox += r * cosf(tmpr);
|
||||||
oy += r * sinf(tmpr);
|
oy += r * sinf(tmpr);
|
||||||
""", 'power=1 dist=1')
|
""", """
|
||||||
|
{{pv._set('cn')}} = {{pv.dist}} / (2.0f * {{pv.power}});
|
||||||
precalc('juliascope',
|
""")
|
||||||
"{{pre._set('cn')}} = {{pre.dist}} / (2.0f * {{pre.power}});\n")
|
|
||||||
|
|
||||||
var(33, 'juliascope', """
|
|
||||||
{{juliascope_precalc(pv)}}
|
|
||||||
|
|
||||||
|
var('juliascope', """
|
||||||
float ang = atan2f(ty, tx);
|
float ang = atan2f(ty, tx);
|
||||||
float power = {{pv.power}};
|
float power = {{pv.power}};
|
||||||
float t_rnd = truncf(mwc_next_01(rctx) * fabsf(power));
|
float t_rnd = truncf(mwc_next_01(rctx) * fabsf(power));
|
||||||
@ -328,16 +304,18 @@ var(33, 'juliascope', """
|
|||||||
|
|
||||||
ox += r * cosf(tmpr);
|
ox += r * cosf(tmpr);
|
||||||
oy += r * sinf(tmpr);
|
oy += r * sinf(tmpr);
|
||||||
""", 'power=1 dist=1')
|
""", """
|
||||||
|
{{pv._set('cn')}} = {{pv.dist}} / (2.0f * {{pv.power}});
|
||||||
|
""")
|
||||||
|
|
||||||
var(34, 'blur', """
|
var('blur', """
|
||||||
float tmpr = mwc_next_01(rctx) * 2.0f * M_PI;
|
float tmpr = mwc_next_01(rctx) * 2.0f * M_PI;
|
||||||
float r = w * mwc_next_01(rctx);
|
float r = w * mwc_next_01(rctx);
|
||||||
ox += r * cosf(tmpr);
|
ox += r * cosf(tmpr);
|
||||||
oy += r * sinf(tmpr);
|
oy += r * sinf(tmpr);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(35, 'gaussian_blur', """
|
var('gaussian_blur', """
|
||||||
float ang = mwc_next_01(rctx) * 2.0f * M_PI;
|
float ang = mwc_next_01(rctx) * 2.0f * M_PI;
|
||||||
// constant factor here is stdev correction for converting to Box-Muller;
|
// constant factor here is stdev correction for converting to Box-Muller;
|
||||||
// np.std(np.sum(np.random.random((1<<30, 4)), axis=1) - 2)
|
// np.std(np.sum(np.random.random((1<<30, 4)), axis=1) - 2)
|
||||||
@ -347,7 +325,7 @@ var(35, 'gaussian_blur', """
|
|||||||
oy += r * sinf(ang);
|
oy += r * sinf(ang);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(36, 'radial_blur', """
|
var('radial_blur', """
|
||||||
float blur_angle = {{pv.angle}} * M_PI * 0.5f;
|
float blur_angle = {{pv.angle}} * M_PI * 0.5f;
|
||||||
float spinvar = sinf(blur_angle);
|
float spinvar = sinf(blur_angle);
|
||||||
float zoomvar = cosf(blur_angle);
|
float zoomvar = cosf(blur_angle);
|
||||||
@ -358,9 +336,9 @@ var(36, 'radial_blur', """
|
|||||||
float rz = zoomvar * r - 1.0f;
|
float rz = zoomvar * r - 1.0f;
|
||||||
ox += ra*cosf(tmpa) + rz*tx;
|
ox += ra*cosf(tmpa) + rz*tx;
|
||||||
oy += ra*sinf(tmpa) + rz*ty;
|
oy += ra*sinf(tmpa) + rz*ty;
|
||||||
""", 'angle')
|
""")
|
||||||
|
|
||||||
var(37, 'pie', """
|
var('pie', """
|
||||||
float slices = {{pv.slices}};
|
float slices = {{pv.slices}};
|
||||||
float sl = truncf(mwc_next_01(rctx) * slices + 0.5f);
|
float sl = truncf(mwc_next_01(rctx) * slices + 0.5f);
|
||||||
float a = {{pv.rotation}} +
|
float a = {{pv.rotation}} +
|
||||||
@ -368,9 +346,9 @@ var(37, 'pie', """
|
|||||||
float r = w * mwc_next_01(rctx);
|
float r = w * mwc_next_01(rctx);
|
||||||
ox += r * cosf(a);
|
ox += r * cosf(a);
|
||||||
oy += r * sinf(a);
|
oy += r * sinf(a);
|
||||||
""", 'slices=6 rotation thickness=0.5')
|
""")
|
||||||
|
|
||||||
var(38, 'ngon', """
|
var('ngon', """
|
||||||
float power = {{pv.power}} * 0.5f;
|
float power = {{pv.power}} * 0.5f;
|
||||||
float b = 2.0f * M_PI / {{pv.sides}};
|
float b = 2.0f * M_PI / {{pv.sides}};
|
||||||
float corners = {{pv.corners}};
|
float corners = {{pv.corners}};
|
||||||
@ -384,9 +362,9 @@ var(38, 'ngon', """
|
|||||||
|
|
||||||
ox += w * tx * amp;
|
ox += w * tx * amp;
|
||||||
oy += w * ty * amp;
|
oy += w * ty * amp;
|
||||||
""", 'sides=5 power=3 circle=1 corners=2')
|
""")
|
||||||
|
|
||||||
var(39, 'curl', """
|
var('curl', """
|
||||||
float c1 = {{pv.c1}};
|
float c1 = {{pv.c1}};
|
||||||
float c2 = {{pv.c2}};
|
float c2 = {{pv.c2}};
|
||||||
|
|
||||||
@ -396,34 +374,34 @@ var(39, 'curl', """
|
|||||||
|
|
||||||
ox += r * (tx*re + ty*im);
|
ox += r * (tx*re + ty*im);
|
||||||
oy += r * (ty*re - tx*im);
|
oy += r * (ty*re - tx*im);
|
||||||
""", 'c1=1 c2')
|
""")
|
||||||
|
|
||||||
var(40, 'rectangles', """
|
var('rectangles', """
|
||||||
float rx = {{pv.x}};
|
float rx = {{pv.x}};
|
||||||
float ry = {{pv.y}};
|
float ry = {{pv.y}};
|
||||||
|
|
||||||
ox += w * ( (rx==0.0f) ? tx : rx * (2.0f * floorf(tx/rx) + 1.0f) - tx);
|
ox += w * ( (rx==0.0f) ? tx : rx * (2.0f * floorf(tx/rx) + 1.0f) - tx);
|
||||||
oy += w * ( (ry==0.0f) ? ty : ry * (2.0f * floorf(ty/ry) + 1.0f) - ty);
|
oy += w * ( (ry==0.0f) ? ty : ry * (2.0f * floorf(ty/ry) + 1.0f) - ty);
|
||||||
""", 'x y')
|
""")
|
||||||
|
|
||||||
var(41, 'arch', """
|
var('arch', """
|
||||||
float ang = mwc_next_01(rctx) * w * M_PI;
|
float ang = mwc_next_01(rctx) * w * M_PI;
|
||||||
|
|
||||||
ox += w * sinf(ang);
|
ox += w * sinf(ang);
|
||||||
oy += w * sinf(ang) * sinf(ang) / cosf(ang);
|
oy += w * sinf(ang) * sinf(ang) / cosf(ang);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(42, 'tangent', """
|
var('tangent', """
|
||||||
ox += w * sinf(tx) / cosf(ty);
|
ox += w * sinf(tx) / cosf(ty);
|
||||||
oy += w * tanf(ty);
|
oy += w * tanf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(43, 'square', """
|
var('square', """
|
||||||
ox += w * (mwc_next_01(rctx) - 0.5f);
|
ox += w * (mwc_next_01(rctx) - 0.5f);
|
||||||
oy += w * (mwc_next_01(rctx) - 0.5f);
|
oy += w * (mwc_next_01(rctx) - 0.5f);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(44, 'rays', """
|
var('rays', """
|
||||||
float ang = w * mwc_next_01(rctx) * M_PI;
|
float ang = w * mwc_next_01(rctx) * M_PI;
|
||||||
float r = w / (tx*tx + ty*ty);
|
float r = w / (tx*tx + ty*ty);
|
||||||
float tanr = w * tanf(ang) * r;
|
float tanr = w * tanf(ang) * r;
|
||||||
@ -431,13 +409,13 @@ var(44, 'rays', """
|
|||||||
oy += tanr * sinf(ty);
|
oy += tanr * sinf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(45, 'blade', """
|
var('blade', """
|
||||||
float r = mwc_next_01(rctx) * w * sqrtf(tx*tx + ty*ty);
|
float r = mwc_next_01(rctx) * w * sqrtf(tx*tx + ty*ty);
|
||||||
ox += w * tx * (cosf(r) + sinf(r));
|
ox += w * tx * (cosf(r) + sinf(r));
|
||||||
oy += w * tx * (cosf(r) - sinf(r));
|
oy += w * tx * (cosf(r) - sinf(r));
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(46, 'secant2', """
|
var('secant2', """
|
||||||
float r = w * sqrtf(tx*tx + ty*ty);
|
float r = w * sqrtf(tx*tx + ty*ty);
|
||||||
float cr = cosf(r);
|
float cr = cosf(r);
|
||||||
float icr = 1.0f / cr;
|
float icr = 1.0f / cr;
|
||||||
@ -449,7 +427,7 @@ var(46, 'secant2', """
|
|||||||
|
|
||||||
# var 47 is twintrian, has a call to badvalue in it
|
# var 47 is twintrian, has a call to badvalue in it
|
||||||
|
|
||||||
var(48, 'cross', """
|
var('cross', """
|
||||||
float s = tx*tx - ty*ty;
|
float s = tx*tx - ty*ty;
|
||||||
float r = w * sqrtf(1.0f / (s*s));
|
float r = w * sqrtf(1.0f / (s*s));
|
||||||
|
|
||||||
@ -457,7 +435,7 @@ var(48, 'cross', """
|
|||||||
oy += r * ty;
|
oy += r * ty;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(49, 'disc2', """
|
var('disc2', """
|
||||||
float twist = {{pv.twist}};
|
float twist = {{pv.twist}};
|
||||||
float rotpi = {{pv.rot}} * M_PI;
|
float rotpi = {{pv.rot}} * M_PI;
|
||||||
|
|
||||||
@ -481,9 +459,9 @@ var(49, 'disc2', """
|
|||||||
|
|
||||||
ox += r * (sinf(t) + costwist);
|
ox += r * (sinf(t) + costwist);
|
||||||
oy += r * (cosf(t) + sintwist);
|
oy += r * (cosf(t) + sintwist);
|
||||||
""", 'rot twist')
|
""")
|
||||||
|
|
||||||
var(50, 'super_shape', """
|
var('super_shape', """
|
||||||
float ang = atan2f(ty, tx);
|
float ang = atan2f(ty, tx);
|
||||||
float theta = 0.25f * ({{pv.m}} * ang + M_PI);
|
float theta = 0.25f * ({{pv.m}} * ang + M_PI);
|
||||||
float t1 = fabsf(cosf(theta));
|
float t1 = fabsf(cosf(theta));
|
||||||
@ -498,9 +476,9 @@ var(50, 'super_shape', """
|
|||||||
|
|
||||||
ox += r * tx;
|
ox += r * tx;
|
||||||
oy += r * ty;
|
oy += r * ty;
|
||||||
""", 'rnd m n1=1 n2=1 n3=1 holes')
|
""")
|
||||||
|
|
||||||
var(51, 'flower', """
|
var('flower', """
|
||||||
float holes = {{pv.holes}};
|
float holes = {{pv.holes}};
|
||||||
float petals = {{pv.petals}};
|
float petals = {{pv.petals}};
|
||||||
|
|
||||||
@ -509,9 +487,9 @@ var(51, 'flower', """
|
|||||||
|
|
||||||
ox += r * tx;
|
ox += r * tx;
|
||||||
oy += r * ty;
|
oy += r * ty;
|
||||||
""", 'holes petals')
|
""")
|
||||||
|
|
||||||
var(52, 'conic', """
|
var('conic', """
|
||||||
float d = sqrtf(tx*tx + ty*ty);
|
float d = sqrtf(tx*tx + ty*ty);
|
||||||
float ct = tx / d;
|
float ct = tx / d;
|
||||||
float holes = {{pv.holes}};
|
float holes = {{pv.holes}};
|
||||||
@ -521,27 +499,27 @@ var(52, 'conic', """
|
|||||||
|
|
||||||
ox += r * tx;
|
ox += r * tx;
|
||||||
oy += r * ty;
|
oy += r * ty;
|
||||||
""", 'holes eccentricity=1')
|
""")
|
||||||
|
|
||||||
var(53, 'parabola', """
|
var('parabola', """
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
float sr = sinf(r);
|
float sr = sinf(r);
|
||||||
float cr = cosf(r);
|
float cr = cosf(r);
|
||||||
|
|
||||||
ox += {{pv.height}} * w * sr * sr * mwc_next_01(rctx);
|
ox += {{pv.height}} * w * sr * sr * mwc_next_01(rctx);
|
||||||
oy += {{pv.width}} * w * cr * mwc_next_01(rctx);
|
oy += {{pv.width}} * w * cr * mwc_next_01(rctx);
|
||||||
""", 'height width')
|
""")
|
||||||
|
|
||||||
var(54, 'bent2', """
|
var('bent2', """
|
||||||
float nx = 1.0f;
|
float nx = 1.0f;
|
||||||
if (tx < 0.0f) nx = {{pv.x}};
|
if (tx < 0.0f) nx = {{pv.x}};
|
||||||
float ny = 1.0f;
|
float ny = 1.0f;
|
||||||
if (ty < 0.0f) ny = {{pv.y}};
|
if (ty < 0.0f) ny = {{pv.y}};
|
||||||
ox += w * nx * tx;
|
ox += w * nx * tx;
|
||||||
oy += w * ny * ty;
|
oy += w * ny * ty;
|
||||||
""", 'x=1 y=1')
|
""")
|
||||||
|
|
||||||
var(55, 'bipolar', """
|
var('bipolar', """
|
||||||
float x2y2 = tx*tx + ty*ty;
|
float x2y2 = tx*tx + ty*ty;
|
||||||
float t = x2y2 + 1.0f;
|
float t = x2y2 + 1.0f;
|
||||||
float x2 = tx * 2.0f;
|
float x2 = tx * 2.0f;
|
||||||
@ -555,9 +533,9 @@ var(55, 'bipolar', """
|
|||||||
|
|
||||||
ox += w * 0.25f * M_2_PI * logf( (t+x2) / (t-x2) );
|
ox += w * 0.25f * M_2_PI * logf( (t+x2) / (t-x2) );
|
||||||
oy += w * M_2_PI * y;
|
oy += w * M_2_PI * y;
|
||||||
""", 'shift')
|
""")
|
||||||
|
|
||||||
var(56, 'boarders', """
|
var('boarders', """
|
||||||
float roundX = rintf(tx);
|
float roundX = rintf(tx);
|
||||||
float roundY = rintf(ty);
|
float roundY = rintf(ty);
|
||||||
float offsetX = tx - roundX;
|
float offsetX = tx - roundX;
|
||||||
@ -587,7 +565,7 @@ var(56, 'boarders', """
|
|||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(57, 'butterfly', """
|
var('butterfly', """
|
||||||
/* wx is weight*4/sqrt(3*pi) */
|
/* wx is weight*4/sqrt(3*pi) */
|
||||||
float wx = w * 1.3029400317411197908970256609023f;
|
float wx = w * 1.3029400317411197908970256609023f;
|
||||||
float y2 = ty * 2.0f;
|
float y2 = ty * 2.0f;
|
||||||
@ -596,7 +574,7 @@ var(57, 'butterfly', """
|
|||||||
oy += r * y2;
|
oy += r * y2;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(58, 'cell', """
|
var('cell', """
|
||||||
float cell_size = {{pv.size}};
|
float cell_size = {{pv.size}};
|
||||||
float inv_cell_size = 1.0f/cell_size;
|
float inv_cell_size = 1.0f/cell_size;
|
||||||
|
|
||||||
@ -629,37 +607,33 @@ var(58, 'cell', """
|
|||||||
|
|
||||||
ox += w * (dx + x*cell_size);
|
ox += w * (dx + x*cell_size);
|
||||||
oy -= w * (dy + y*cell_size);
|
oy -= w * (dy + y*cell_size);
|
||||||
""", 'size=1')
|
""")
|
||||||
|
|
||||||
var(59, 'cpow', """
|
var('cpow', """
|
||||||
float a = atan2f(ty, tx);
|
float a = atan2f(ty, tx);
|
||||||
float lnr = 0.5f * logf(tx*tx+ty*ty);
|
float lnr = 0.5f * logf(tx*tx+ty*ty);
|
||||||
float power = {{pv.power}};
|
float power = 1.0f / {{pv.power}};
|
||||||
float va = 2.0f * M_PI / power;
|
float va = 2.0f * M_PI * power;
|
||||||
float vc = {{pv.cpow_r}} / power;
|
float vc = {{pv.r}} * power;
|
||||||
float vd = {{pv.cpow_i}} / power;
|
float vd = {{pv.i}} * power;
|
||||||
float ang = vc*a + vd*lnr + va*floorf(power*mwc_next_01(rctx));
|
float ang = vc*a + vd*lnr + va*floorf(power*mwc_next_01(rctx));
|
||||||
float m = w * expf(vc * lnr - vd * a);
|
float m = w * expf(vc * lnr - vd * a);
|
||||||
ox += m * cosf(ang);
|
ox += m * cosf(ang);
|
||||||
oy += m * sinf(ang);
|
oy += m * sinf(ang);
|
||||||
""", 'r=1 i power=1')
|
""")
|
||||||
|
|
||||||
|
var('curve', """
|
||||||
precalc('curve', '''
|
|
||||||
float xl = {{pv.xlength}}, yl = {{pv.ylength}};
|
|
||||||
{{pre._set('x2')}} = 1.0f / max(1e-20f, xl * xl);
|
|
||||||
{{pre._set('y2')}} = 1.0f / max(1e-20f, yl * yl);
|
|
||||||
''')
|
|
||||||
|
|
||||||
var(60, 'curve', """
|
|
||||||
{{curve_precalc()}}
|
|
||||||
float pc_xlen = {{pv.x2}}, pc_ylen = {{pv.y2}};
|
float pc_xlen = {{pv.x2}}, pc_ylen = {{pv.y2}};
|
||||||
|
|
||||||
ox += w * (tx + {{pv.xamp}} * expf(-ty*ty*pc_xlen));
|
ox += w * (tx + {{pv.xamp}} * expf(-ty*ty*pc_xlen));
|
||||||
oy += w * (ty + {{pv.yamp}} * expf(-tx*tx*pc_ylen));
|
oy += w * (ty + {{pv.yamp}} * expf(-tx*tx*pc_ylen));
|
||||||
""", 'xamp yamp xlength=1 ylength=1')
|
""", """
|
||||||
|
float xl = {{pv.xlength}}, yl = {{pv.ylength}};
|
||||||
|
{{pv._set('x2')}} = 1.0f / max(1e-20f, xl * xl);
|
||||||
|
{{pv._set('y2')}} = 1.0f / max(1e-20f, yl * yl);
|
||||||
|
""")
|
||||||
|
|
||||||
var(61, 'edisc', """
|
var('edisc', """
|
||||||
float tmp = tx*tx + ty*ty + 1.0f;
|
float tmp = tx*tx + ty*ty + 1.0f;
|
||||||
float tmp2 = 2.0f * tx;
|
float tmp2 = 2.0f * tx;
|
||||||
float r1 = sqrtf(tmp+tmp2);
|
float r1 = sqrtf(tmp+tmp2);
|
||||||
@ -677,7 +651,7 @@ var(61, 'edisc', """
|
|||||||
oy += neww * sinhf(a2) * snv;
|
oy += neww * sinhf(a2) * snv;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(62, 'elliptic', """
|
var('elliptic', """
|
||||||
float tmp = tx*tx + ty*ty + 1.0f;
|
float tmp = tx*tx + ty*ty + 1.0f;
|
||||||
float x2 = 2.0f * tx;
|
float x2 = 2.0f * tx;
|
||||||
float xmax = 0.5f * (sqrtf(tmp+x2) + sqrtf(tmp-x2));
|
float xmax = 0.5f * (sqrtf(tmp+x2) + sqrtf(tmp-x2));
|
||||||
@ -704,7 +678,7 @@ var(62, 'elliptic', """
|
|||||||
oy -= neww * logf(xmax + ssx);
|
oy -= neww * logf(xmax + ssx);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(63, 'escher', """
|
var('escher', """
|
||||||
float a = atan2f(ty,tx);
|
float a = atan2f(ty,tx);
|
||||||
float lnr = 0.5f * logf(tx*tx + ty*ty);
|
float lnr = 0.5f * logf(tx*tx + ty*ty);
|
||||||
float ebeta = {{pv.beta}};
|
float ebeta = {{pv.beta}};
|
||||||
@ -717,9 +691,9 @@ var(63, 'escher', """
|
|||||||
|
|
||||||
ox += m * cosf(n);
|
ox += m * cosf(n);
|
||||||
oy += m * sinf(n);
|
oy += m * sinf(n);
|
||||||
""", 'beta')
|
""")
|
||||||
|
|
||||||
var(64, 'foci', """
|
var('foci', """
|
||||||
float expx = expf(tx) * 0.5f;
|
float expx = expf(tx) * 0.5f;
|
||||||
float expnx = 0.25f / expx;
|
float expnx = 0.25f / expx;
|
||||||
float sn = sinf(ty);
|
float sn = sinf(ty);
|
||||||
@ -729,7 +703,7 @@ var(64, 'foci', """
|
|||||||
oy += tmp * sn;
|
oy += tmp * sn;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(65, 'lazysusan', """
|
var('lazysusan', """
|
||||||
float lx = {{pv.x}};
|
float lx = {{pv.x}};
|
||||||
float ly = {{pv.y}};
|
float ly = {{pv.y}};
|
||||||
float x = tx - lx;
|
float x = tx - lx;
|
||||||
@ -748,9 +722,9 @@ var(65, 'lazysusan', """
|
|||||||
ox += w * (r * x + lx);
|
ox += w * (r * x + lx);
|
||||||
oy += w * (r * y - ly);
|
oy += w * (r * y - ly);
|
||||||
}
|
}
|
||||||
""", 'x y twist space spin')
|
""")
|
||||||
|
|
||||||
var(66, 'loonie', """
|
var('loonie', """
|
||||||
float r2 = tx*tx + ty*ty;;
|
float r2 = tx*tx + ty*ty;;
|
||||||
float w2 = w*w;
|
float w2 = w*w;
|
||||||
|
|
||||||
@ -764,7 +738,7 @@ var(66, 'loonie', """
|
|||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(67, 'pre_blur', """
|
var('pre_blur', """
|
||||||
float rndG = w * (mwc_next_01(rctx) + mwc_next_01(rctx)
|
float rndG = w * (mwc_next_01(rctx) + mwc_next_01(rctx)
|
||||||
+ mwc_next_01(rctx) + mwc_next_01(rctx) - 2.0f);
|
+ mwc_next_01(rctx) + mwc_next_01(rctx) - 2.0f);
|
||||||
float rndA = mwc_next_01(rctx) * 2.0f * M_PI;
|
float rndA = mwc_next_01(rctx) * 2.0f * M_PI;
|
||||||
@ -774,7 +748,7 @@ var(67, 'pre_blur', """
|
|||||||
ty += rndG * sinf(rndA);
|
ty += rndG * sinf(rndA);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(68, 'modulus', """
|
var('modulus', """
|
||||||
float mx = {{pv.x}}, my = {{pv.y}};
|
float mx = {{pv.x}}, my = {{pv.y}};
|
||||||
float xr = 2.0f*mx;
|
float xr = 2.0f*mx;
|
||||||
float yr = 2.0f*my;
|
float yr = 2.0f*my;
|
||||||
@ -792,9 +766,9 @@ var(68, 'modulus', """
|
|||||||
oy += w * ( my - fmodf(my - ty, yr));
|
oy += w * ( my - fmodf(my - ty, yr));
|
||||||
else
|
else
|
||||||
oy += w * ty;
|
oy += w * ty;
|
||||||
""", 'x y')
|
""")
|
||||||
|
|
||||||
var(69, 'oscope', """
|
var('oscope', """
|
||||||
float tpf = 2.0f * M_PI * {{pv.frequency}};
|
float tpf = 2.0f * M_PI * {{pv.frequency}};
|
||||||
float amp = {{pv.amplitude}};
|
float amp = {{pv.amplitude}};
|
||||||
float sep = {{pv.separation}};
|
float sep = {{pv.separation}};
|
||||||
@ -807,21 +781,21 @@ var(69, 'oscope', """
|
|||||||
oy -= w*ty;
|
oy -= w*ty;
|
||||||
else
|
else
|
||||||
oy += w*ty;
|
oy += w*ty;
|
||||||
""", 'separation=1 frequency=M_PI amplitude=1 damping')
|
""")
|
||||||
|
|
||||||
var(70, 'polar2', """
|
var('polar2', """
|
||||||
float p2v = w / M_PI;
|
float p2v = w / M_PI;
|
||||||
ox += p2v * atan2f(tx,ty);
|
ox += p2v * atan2f(tx,ty);
|
||||||
oy += 0.5f * p2v * logf(tx*tx + ty*ty);
|
oy += 0.5f * p2v * logf(tx*tx + ty*ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(71, 'popcorn2', """
|
var('popcorn2', """
|
||||||
float c = {{pv.c}};
|
float c = {{pv.c}};
|
||||||
ox += w * (tx + {{pv.x}} * sinf(tanf(ty*c)));
|
ox += w * (tx + {{pv.x}} * sinf(tanf(ty*c)));
|
||||||
oy += w * (ty + {{pv.y}} * sinf(tanf(tx*c)));
|
oy += w * (ty + {{pv.y}} * sinf(tanf(tx*c)));
|
||||||
""", 'x y c')
|
""")
|
||||||
|
|
||||||
var(72, 'scry', """
|
var('scry', """
|
||||||
/* note that scry does not multiply by weight, but as the */
|
/* note that scry does not multiply by weight, but as the */
|
||||||
/* values still approach 0 as the weight approaches 0, it */
|
/* values still approach 0 as the weight approaches 0, it */
|
||||||
/* should be ok */
|
/* should be ok */
|
||||||
@ -831,7 +805,7 @@ var(72, 'scry', """
|
|||||||
oy += ty*r;
|
oy += ty*r;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(73, 'separation', """
|
var('separation', """
|
||||||
float sx2 = {{pv.x}} * {{pv.x}};
|
float sx2 = {{pv.x}} * {{pv.x}};
|
||||||
float sy2 = {{pv.y}} * {{pv.y}};
|
float sy2 = {{pv.y}} * {{pv.y}};
|
||||||
|
|
||||||
@ -844,9 +818,9 @@ var(73, 'separation', """
|
|||||||
oy += w * (sqrtf(ty*ty + sy2) - ty*{{pv.yinside}});
|
oy += w * (sqrtf(ty*ty + sy2) - ty*{{pv.yinside}});
|
||||||
else
|
else
|
||||||
oy -= w * (sqrtf(ty*ty + sy2) + ty*{{pv.yinside}});
|
oy -= w * (sqrtf(ty*ty + sy2) + ty*{{pv.yinside}});
|
||||||
""", 'x xinside y yinside')
|
""")
|
||||||
|
|
||||||
var(74, 'split', """
|
var('split', """
|
||||||
if (cosf(tx*{{pv.xsize}}*M_PI) >= 0.0f)
|
if (cosf(tx*{{pv.xsize}}*M_PI) >= 0.0f)
|
||||||
oy += w*ty;
|
oy += w*ty;
|
||||||
else
|
else
|
||||||
@ -856,21 +830,21 @@ var(74, 'split', """
|
|||||||
ox += w*tx;
|
ox += w*tx;
|
||||||
else
|
else
|
||||||
ox -= w*tx;
|
ox -= w*tx;
|
||||||
""", 'xsize ysize')
|
""")
|
||||||
|
|
||||||
var(75, 'splits', """
|
var('splits', """
|
||||||
ox += w*(tx + copysignf({{pv.x}}, tx));
|
ox += w*(tx + copysignf({{pv.x}}, tx));
|
||||||
oy += w*(ty + copysignf({{pv.y}}, ty));
|
oy += w*(ty + copysignf({{pv.y}}, ty));
|
||||||
""", 'x y')
|
""")
|
||||||
|
|
||||||
var(76, 'stripes', """
|
var('stripes', """
|
||||||
float roundx = floorf(tx + 0.5f);
|
float roundx = floorf(tx + 0.5f);
|
||||||
float offsetx = tx - roundx;
|
float offsetx = tx - roundx;
|
||||||
ox += w * (offsetx * (1.0f - {{pv.space}}) + roundx);
|
ox += w * (offsetx * (1.0f - {{pv.space}}) + roundx);
|
||||||
oy += w * (ty + offsetx*offsetx*{{pv.warp}});
|
oy += w * (ty + offsetx*offsetx*{{pv.warp}});
|
||||||
""", 'space warp')
|
""")
|
||||||
|
|
||||||
var(77, 'wedge', """
|
var('wedge', """
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
float a = atan2f(ty, tx) + {{pv.swirl}} * r;
|
float a = atan2f(ty, tx) + {{pv.swirl}} * r;
|
||||||
float wc = {{pv.count}};
|
float wc = {{pv.count}};
|
||||||
@ -881,9 +855,9 @@ var(77, 'wedge', """
|
|||||||
r = w * (r + {{pv.hole}});
|
r = w * (r + {{pv.hole}});
|
||||||
ox += r * cosf(a);
|
ox += r * cosf(a);
|
||||||
oy += r * sinf(a);
|
oy += r * sinf(a);
|
||||||
""", 'angle hole count=1 swirl')
|
""")
|
||||||
|
|
||||||
var(80, 'whorl', """
|
var('whorl', """
|
||||||
float r = sqrtf(tx*tx + ty*ty);
|
float r = sqrtf(tx*tx + ty*ty);
|
||||||
float a = atan2f(ty,tx);
|
float a = atan2f(ty,tx);
|
||||||
|
|
||||||
@ -894,93 +868,93 @@ var(80, 'whorl', """
|
|||||||
|
|
||||||
ox += w * r * cosf(a);
|
ox += w * r * cosf(a);
|
||||||
oy += w * r * sinf(a);
|
oy += w * r * sinf(a);
|
||||||
""", 'inside outside')
|
""")
|
||||||
|
|
||||||
var(81, 'waves2', """
|
var('waves2', """
|
||||||
ox += w*(tx + {{pv.scalex}}*sinf(ty * {{pv.freqx}}));
|
ox += w*(tx + {{pv.scalex}}*sinf(ty * {{pv.freqx}}));
|
||||||
oy += w*(ty + {{pv.scaley}}*sinf(tx * {{pv.freqy}}));
|
oy += w*(ty + {{pv.scaley}}*sinf(tx * {{pv.freqy}}));
|
||||||
""", 'scalex scaley freqx freqy')
|
""")
|
||||||
|
|
||||||
var(82, 'exp', """
|
var('exp', """
|
||||||
float expe = expf(tx);
|
float expe = expf(tx);
|
||||||
ox += w * expe * cosf(ty);
|
ox += w * expe * cosf(ty);
|
||||||
oy += w * expe * sinf(ty);
|
oy += w * expe * sinf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(83, 'log', """
|
var('log', """
|
||||||
ox += w * 0.5f * logf(tx*tx + ty*ty);
|
ox += w * 0.5f * logf(tx*tx + ty*ty);
|
||||||
oy += w * atan2f(ty, tx);
|
oy += w * atan2f(ty, tx);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(84, 'sin', """
|
var('sin', """
|
||||||
ox += w * sinf(tx) * coshf(ty);
|
ox += w * sinf(tx) * coshf(ty);
|
||||||
oy += w * cosf(tx) * sinhf(ty);
|
oy += w * cosf(tx) * sinhf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(85, 'cos', """
|
var('cos', """
|
||||||
ox += w * cosf(tx) * coshf(ty);
|
ox += w * cosf(tx) * coshf(ty);
|
||||||
oy -= w * sinf(tx) * sinhf(ty);
|
oy -= w * sinf(tx) * sinhf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(86, 'tan', """
|
var('tan', """
|
||||||
float tanden = 1.0f/(cosf(2.0f*tx) + coshf(2.0f*ty));
|
float tanden = 1.0f/(cosf(2.0f*tx) + coshf(2.0f*ty));
|
||||||
ox += w * tanden * sinf(2.0f*tx);
|
ox += w * tanden * sinf(2.0f*tx);
|
||||||
oy += w * tanden * sinhf(2.0f*ty);
|
oy += w * tanden * sinhf(2.0f*ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(87, 'sec', """
|
var('sec', """
|
||||||
float secden = 2.0f/(cosf(2.0f*tx) + coshf(2.0f*ty));
|
float secden = 2.0f/(cosf(2.0f*tx) + coshf(2.0f*ty));
|
||||||
ox += w * secden * cosf(tx) * coshf(ty);
|
ox += w * secden * cosf(tx) * coshf(ty);
|
||||||
oy += w * secden * sinf(tx) * sinhf(ty);
|
oy += w * secden * sinf(tx) * sinhf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(88, 'csc', """
|
var('csc', """
|
||||||
float cscden = 2.0f/(coshf(2.0f*ty) - cosf(2.0f*tx));
|
float cscden = 2.0f/(coshf(2.0f*ty) - cosf(2.0f*tx));
|
||||||
ox += w * cscden * sinf(tx) * coshf(ty);
|
ox += w * cscden * sinf(tx) * coshf(ty);
|
||||||
oy -= w * cscden * cosf(tx) * sinhf(ty);
|
oy -= w * cscden * cosf(tx) * sinhf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(89, 'cot', """
|
var('cot', """
|
||||||
float cotden = 1.0f/(coshf(2.0f*ty) - cosf(2.0f*tx));
|
float cotden = 1.0f/(coshf(2.0f*ty) - cosf(2.0f*tx));
|
||||||
ox += w * cotden * sinf(2.0f*tx);
|
ox += w * cotden * sinf(2.0f*tx);
|
||||||
oy += w * cotden * -1.0f * sinhf(2.0f*ty);
|
oy += w * cotden * -1.0f * sinhf(2.0f*ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(90, 'sinh', """
|
var('sinh', """
|
||||||
ox += w * sinhf(tx) * cosf(ty);
|
ox += w * sinhf(tx) * cosf(ty);
|
||||||
oy += w * coshf(tx) * sinf(ty);
|
oy += w * coshf(tx) * sinf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(91, 'cosh', """
|
var('cosh', """
|
||||||
ox += w * coshf(tx) * cosf(ty);
|
ox += w * coshf(tx) * cosf(ty);
|
||||||
oy += w * sinhf(tx) * sinf(ty);
|
oy += w * sinhf(tx) * sinf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(92, 'tanh', """
|
var('tanh', """
|
||||||
float tanhden = 1.0f/(cosf(2.0f*ty) + coshf(2.0f*tx));
|
float tanhden = 1.0f/(cosf(2.0f*ty) + coshf(2.0f*tx));
|
||||||
ox += w * tanhden * sinhf(2.0f*tx);
|
ox += w * tanhden * sinhf(2.0f*tx);
|
||||||
oy += w * tanhden * sinf(2.0f*ty);
|
oy += w * tanhden * sinf(2.0f*ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(93, 'sech', """
|
var('sech', """
|
||||||
float sechden = 2.0f/(cosf(2.0f*ty) + coshf(2.0f*tx));
|
float sechden = 2.0f/(cosf(2.0f*ty) + coshf(2.0f*tx));
|
||||||
ox += w * sechden * cosf(ty) * coshf(tx);
|
ox += w * sechden * cosf(ty) * coshf(tx);
|
||||||
oy -= w * sechden * sinf(ty) * sinhf(tx);
|
oy -= w * sechden * sinf(ty) * sinhf(tx);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(94, 'csch', """
|
var('csch', """
|
||||||
float cschden = 2.0f/(coshf(2.0f*tx) - cosf(2.0f*ty));
|
float cschden = 2.0f/(coshf(2.0f*tx) - cosf(2.0f*ty));
|
||||||
ox += w * cschden * sinhf(tx) * cosf(ty);
|
ox += w * cschden * sinhf(tx) * cosf(ty);
|
||||||
oy -= w * cschden * coshf(tx) * sinf(ty);
|
oy -= w * cschden * coshf(tx) * sinf(ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(95, 'coth', """
|
var('coth', """
|
||||||
float cothden = 1.0f/(coshf(2.0f*tx) - cosf(2.0f*ty));
|
float cothden = 1.0f/(coshf(2.0f*tx) - cosf(2.0f*ty));
|
||||||
ox += w * cothden * sinhf(2.0f*tx);
|
ox += w * cothden * sinhf(2.0f*tx);
|
||||||
oy += w * cothden * sinf(2.0f*ty);
|
oy += w * cothden * sinf(2.0f*ty);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var(97, 'flux', """
|
var('flux', """
|
||||||
float xpw = tx + w;
|
float xpw = tx + w;
|
||||||
float xmw = tx - w;
|
float xmw = tx - w;
|
||||||
float avgr = w * (2.0f + {{pv.spread}})
|
float avgr = w * (2.0f + {{pv.spread}})
|
||||||
@ -988,9 +962,9 @@ var(97, 'flux', """
|
|||||||
float avga = (atan2f(ty, xmw) - atan2f(ty,xpw))*0.5f;
|
float avga = (atan2f(ty, xmw) - atan2f(ty,xpw))*0.5f;
|
||||||
ox += avgr * cosf(avga);
|
ox += avgr * cosf(avga);
|
||||||
oy += avgr * sinf(avga);
|
oy += avgr * sinf(avga);
|
||||||
""", 'spread')
|
""")
|
||||||
|
|
||||||
var(98, 'mobius', """
|
var('mobius', """
|
||||||
float rea = {{pv.re_a}};
|
float rea = {{pv.re_a}};
|
||||||
float ima = {{pv.im_a}};
|
float ima = {{pv.im_a}};
|
||||||
float reb = {{pv.re_b}};
|
float reb = {{pv.re_b}};
|
||||||
@ -1011,5 +985,4 @@ var(98, 'mobius', """
|
|||||||
|
|
||||||
ox += rad_v * (re_u*re_v + im_u*im_v);
|
ox += rad_v * (re_u*re_v + im_u*im_v);
|
||||||
oy += rad_v * (im_u*re_v - re_u*im_v);
|
oy += rad_v * (im_u*re_v - re_u*im_v);
|
||||||
""", 're_a im_a re_b im_b re_c im_c re_d im_d')
|
""")
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ def mkdsc(dim, ch):
|
|||||||
format=cuda.array_format.FLOAT)
|
format=cuda.array_format.FLOAT)
|
||||||
|
|
||||||
class Filter(object):
|
class Filter(object):
|
||||||
def apply(self, fb, gnm, dim, tc, stream=None):
|
def apply(self, fb, gprof, params, dim, tc, stream=None):
|
||||||
"""
|
"""
|
||||||
Queue the application of this filter. When the live stream finishes
|
Queue the application of this filter. When the live stream finishes
|
||||||
executing the last item enqueued by this method, the result must be
|
executing the last item enqueued by this method, the result must be
|
||||||
@ -32,15 +32,10 @@ class Filter(object):
|
|||||||
|
|
||||||
class Bilateral(Filter, ClsMod):
|
class Bilateral(Filter, ClsMod):
|
||||||
lib = code.filters.bilaterallib
|
lib = code.filters.bilaterallib
|
||||||
def __init__(self, directions=8, r=15, sstd=6, cstd=0.05,
|
radius = 15
|
||||||
dstd=1.5, dpow=0.8, gspeed=4.0):
|
directions = 8
|
||||||
# TODO: expose these parameters on the genome, or at least on the
|
|
||||||
# profile, and set them by a less ugly mechanism
|
|
||||||
for n in 'directions r sstd cstd dstd dpow gspeed'.split():
|
|
||||||
setattr(self, n, locals()[n])
|
|
||||||
super(Bilateral, self).__init__()
|
|
||||||
|
|
||||||
def apply(self, fb, gnm, dim, tc, stream=None):
|
def apply(self, fb, gprof, params, dim, tc, stream=None):
|
||||||
# Helper variables and functions to keep it clean
|
# Helper variables and functions to keep it clean
|
||||||
sb = 16 * dim.astride
|
sb = 16 * dim.astride
|
||||||
bs = sb * dim.ah
|
bs = sb * dim.ah
|
||||||
@ -53,7 +48,7 @@ class Bilateral(Filter, ClsMod):
|
|||||||
for pattern in range(self.directions):
|
for pattern in range(self.directions):
|
||||||
# Scale spatial parameter so that a "pixel" is equivalent to an
|
# Scale spatial parameter so that a "pixel" is equivalent to an
|
||||||
# actual pixel at 1080p
|
# actual pixel at 1080p
|
||||||
sstd = self.sstd * dim.w / 1920.
|
sstd = params.spatial_std(tc) * dim.w / 1920.
|
||||||
|
|
||||||
tref.set_address_2d(fb.d_front, dsc, sb)
|
tref.set_address_2d(fb.d_front, dsc, sb)
|
||||||
|
|
||||||
@ -67,38 +62,39 @@ class Bilateral(Filter, ClsMod):
|
|||||||
grad_tref.set_address_2d(fb.d_side, grad_dsc, sb / 4)
|
grad_tref.set_address_2d(fb.d_side, grad_dsc, sb / 4)
|
||||||
|
|
||||||
launch2('bilateral', self.mod, stream, dim,
|
launch2('bilateral', self.mod, stream, dim,
|
||||||
fb.d_back, i32(pattern), i32(self.r),
|
fb.d_back, i32(pattern), i32(self.radius),
|
||||||
f32(sstd), f32(self.cstd), f32(self.dstd),
|
f32(sstd), f32(params.color_std(tc)),
|
||||||
f32(self.dpow), f32(self.gspeed),
|
f32(params.density_std(tc)), f32(params.density_pow(tc)),
|
||||||
|
f32(params.gradient(tc)),
|
||||||
texrefs=[tref, grad_tref])
|
texrefs=[tref, grad_tref])
|
||||||
fb.flip()
|
fb.flip()
|
||||||
|
|
||||||
class Logscale(Filter, ClsMod):
|
class Logscale(Filter, ClsMod):
|
||||||
lib = code.filters.logscalelib
|
lib = code.filters.logscalelib
|
||||||
def apply(self, fb, gnm, dim, tc, stream=None):
|
def apply(self, fb, gprof, params, dim, tc, stream=None):
|
||||||
"""Log-scale in place."""
|
"""Log-scale in place."""
|
||||||
k1 = f32(gnm.color.brightness(tc) * 268 / 256)
|
k1 = f32(params.brightness(tc) * 268 / 256)
|
||||||
# Old definition of area is (w*h/(s*s)). Since new scale 'ns' is now
|
# Old definition of area is (w*h/(s*s)). Since new scale 'ns' is now
|
||||||
# s/w, new definition is (w*h/(s*s*w*w)) = (h/(s*s*w))
|
# s/w, new definition is (w*h/(s*s*w*w)) = (h/(s*s*w))
|
||||||
area = dim.h / (gnm.camera.scale(tc) ** 2 * dim.w)
|
area = dim.h / (params.scale(tc) ** 2 * dim.w)
|
||||||
k2 = f32(1.0 / (area * gnm.spp(tc)))
|
k2 = f32(1.0 / (area * gprof.spp(tc)))
|
||||||
launch2('logscale', self.mod, stream, dim,
|
launch2('logscale', self.mod, stream, dim,
|
||||||
fb.d_front, fb.d_front, k1, k2)
|
fb.d_front, fb.d_front, k1, k2)
|
||||||
|
|
||||||
class HaloClip(Filter, ClsMod):
|
class HaloClip(Filter, ClsMod):
|
||||||
lib = code.filters.halocliplib
|
lib = code.filters.halocliplib
|
||||||
def apply(self, fb, gnm, dim, tc, stream=None):
|
def apply(self, fb, gprof, params, dim, tc, stream=None):
|
||||||
gam = f32(1 / gnm.color.gamma(tc) - 1)
|
gam = f32(1 / params.gamma(tc) - 1)
|
||||||
|
|
||||||
dsc = mkdsc(dim, 1)
|
dsc = mkdsc(dim, 1)
|
||||||
tref = mktref(self.mod, 'chan1_src')
|
tref = mktref(self.mod, 'chan1_src')
|
||||||
|
|
||||||
launch2('apply_gamma', self.mod, stream, dim,
|
launch2('apply_gamma', self.mod, stream, dim,
|
||||||
fb.d_side, fb.d_front, gam)
|
fb.d_side, fb.d_front, gam)
|
||||||
tref.set_address_2d(fb.d_side, dsc, 4 * dim.astride)
|
tref.set_address_2d(fb.d_side, dsc, 4 * params.astride)
|
||||||
launch2('den_blur_1c', self.mod, stream, dim,
|
launch2('den_blur_1c', self.mod, stream, dim,
|
||||||
fb.d_back, i32(0), i32(0), texrefs=[tref])
|
fb.d_back, i32(0), i32(0), texrefs=[tref])
|
||||||
tref.set_address_2d(fb.d_back, dsc, 4 * dim.astride)
|
tref.set_address_2d(fb.d_back, dsc, 4 * params.astride)
|
||||||
launch2('den_blur_1c', self.mod, stream, dim,
|
launch2('den_blur_1c', self.mod, stream, dim,
|
||||||
fb.d_side, i32(1), i32(0), texrefs=[tref])
|
fb.d_side, i32(1), i32(0), texrefs=[tref])
|
||||||
|
|
||||||
@ -107,17 +103,22 @@ class HaloClip(Filter, ClsMod):
|
|||||||
|
|
||||||
class ColorClip(Filter, ClsMod):
|
class ColorClip(Filter, ClsMod):
|
||||||
lib = code.filters.colorcliplib
|
lib = code.filters.colorcliplib
|
||||||
def apply(self, fb, gnm, dim, tc, stream=None):
|
def apply(self, fb, gprof, params, dim, tc, stream=None):
|
||||||
# TODO: implement integration over cubic splines?
|
# TODO: implement integration over cubic splines?
|
||||||
gam = f32(1 / gnm.color.gamma(tc))
|
gam = f32(1 / params.gamma(tc))
|
||||||
vib = f32(gnm.color.vibrance(tc))
|
vib = f32(params.vibrance(tc))
|
||||||
hipow = f32(gnm.color.highlight_power(tc))
|
hipow = f32(params.highlight_power(tc))
|
||||||
lin = f32(gnm.color.gamma_threshold(tc))
|
lin = f32(params.gamma_threshold(tc))
|
||||||
lingam = f32(lin ** (gam-1.0) if lin > 0 else 0)
|
lingam = f32(lin ** (gam-1.0) if lin > 0 else 0)
|
||||||
bkgd = vec.make_float3(
|
|
||||||
gnm.color.background.r(tc),
|
|
||||||
gnm.color.background.g(tc),
|
|
||||||
gnm.color.background.b(tc))
|
|
||||||
|
|
||||||
launch2('colorclip', self.mod, stream, dim,
|
launch2('colorclip', self.mod, stream, dim,
|
||||||
fb.d_front, gam, vib, hipow, lin, lingam, bkgd)
|
fb.d_front, gam, vib, hipow, lin, lingam)
|
||||||
|
|
||||||
|
# Ungainly but practical.
|
||||||
|
filter_map = dict(bilateral=Bilateral, logscale=Logscale, haloclip=HaloClip,
|
||||||
|
colorclip=ColorClip)
|
||||||
|
def create(gprof):
|
||||||
|
# TODO: redesign this (should not have to care about internals of
|
||||||
|
# use.Wrapper in order to find types from TypedList elements)
|
||||||
|
filts = gprof._val.get('filters') or gprof.spec['filters'].defaults
|
||||||
|
return [filter_map[f['type']]() for f in filts]
|
||||||
|
473
cuburn/genome.py
473
cuburn/genome.py
@ -1,473 +0,0 @@
|
|||||||
#!/usr/bin/env python2
|
|
||||||
|
|
||||||
import base64
|
|
||||||
import warnings
|
|
||||||
import xml.parsers.expat
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from code.variations import var_code, var_params
|
|
||||||
from code.util import crep
|
|
||||||
|
|
||||||
class SplEval(object):
|
|
||||||
_mat = np.matrix([[1.,-2, 1, 0], [2,-3, 0, 1],
|
|
||||||
[1,-1, 0, 0], [-2, 3, 0, 0]])
|
|
||||||
_deriv = np.matrix(np.diag([3,2,1], 1))
|
|
||||||
|
|
||||||
def __init__(self, knots, v0=None, v1=None):
|
|
||||||
self.knots = self.normalize(knots, v0, v1)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def normalize(knots, v0=None, v1=None):
|
|
||||||
if isinstance(knots, (int, float)):
|
|
||||||
knots = [0.0, knots, 1.0, knots]
|
|
||||||
elif not np.all(np.diff(np.float32(np.asarray(knots))[::2]) > 0):
|
|
||||||
raise ValueError("Spline times are non-monotonic. (Use "
|
|
||||||
"nextafterf()-spaced times to anchor tangents.)")
|
|
||||||
|
|
||||||
# If stabilizing knots are missing before or after the edges of the
|
|
||||||
# [0,1] interval, add them.
|
|
||||||
if knots[0] >= 0:
|
|
||||||
if v0 is None:
|
|
||||||
v0 = (knots[3] - knots[1]) / float(knots[2] - knots[0])
|
|
||||||
knots = [-2, knots[3] - (knots[2] + 2) * v0] + knots
|
|
||||||
if knots[-2] <= 1:
|
|
||||||
if v1 is None:
|
|
||||||
v1 = (knots[-1] - knots[-3]) / float(knots[-2] - knots[-4])
|
|
||||||
knots.extend([3, knots[-3] + (3 - knots[-4]) * v1])
|
|
||||||
|
|
||||||
knotarray = np.zeros((2, len(knots)/2))
|
|
||||||
knotarray.T.flat[:] = knots
|
|
||||||
return knotarray
|
|
||||||
|
|
||||||
def find_knots(self, itime):
|
|
||||||
idx = np.searchsorted(self.knots[0], itime) - 2
|
|
||||||
idx = max(0, min(idx, len(self.knots[0]) - 4))
|
|
||||||
|
|
||||||
times = self.knots[0][idx:idx+4]
|
|
||||||
vals = self.knots[1][idx:idx+4]
|
|
||||||
# Normalize to [0,1]
|
|
||||||
t = itime - times[1]
|
|
||||||
times = times - times[1]
|
|
||||||
scale = 1 / times[2]
|
|
||||||
t = t * scale
|
|
||||||
times = times * scale
|
|
||||||
return times, vals, t, scale
|
|
||||||
|
|
||||||
def __call__(self, itime, deriv=0):
|
|
||||||
times, vals, t, scale = self.find_knots(itime)
|
|
||||||
|
|
||||||
m1 = (vals[2] - vals[0]) / (1.0 - times[0])
|
|
||||||
m2 = (vals[3] - vals[1]) / times[3]
|
|
||||||
|
|
||||||
mat = self._mat
|
|
||||||
if deriv:
|
|
||||||
mat = mat * (scale * self._deriv) ** deriv
|
|
||||||
val = [m1, vals[1], m2, vals[2]] * mat * np.array([[t**3, t**2, t, 1]]).T
|
|
||||||
return val[0,0]
|
|
||||||
|
|
||||||
def _plt(self, name='SplEval', fig=111, show=True):
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
x = np.linspace(-0.0, 1.0, 500)
|
|
||||||
r = x[1] - x[0]
|
|
||||||
plt.figure(fig)
|
|
||||||
plt.title(name)
|
|
||||||
plt.plot(x,map(self,x),x,[self(i,1) for i in x],'--',
|
|
||||||
self.knots[0],self.knots[1],'x')
|
|
||||||
plt.xlim(0.0, 1.0)
|
|
||||||
if show:
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '[%g:%g]' % (self(0), self(1))
|
|
||||||
def __repr__(self):
|
|
||||||
return '<interp [%g:%g]>' % (self(0), self(1))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def knotlist(self):
|
|
||||||
# TODO: scale error constants proportional to RMS?
|
|
||||||
# If everything is constant, return a constant
|
|
||||||
if np.std(self.knots[1]) < 1e-6:
|
|
||||||
return self.knots[1][0]
|
|
||||||
# If constant slope, omit the end knots
|
|
||||||
slopes = np.diff(self.knots[1]) / np.diff(self.knots[0])
|
|
||||||
if np.std(slopes) < 1e-6:
|
|
||||||
return list(self.knots.T.flat)[2:-2]
|
|
||||||
return list(self.knots.T.flat)
|
|
||||||
|
|
||||||
def update(self, knots, overwrite=True):
|
|
||||||
"""
|
|
||||||
Update this spline's knotlist with ``knots``, a list of two-tuples
|
|
||||||
(time, value) or a dictionary of the same, while preserving the zeroth
|
|
||||||
and first derivatives at t=0 and t=1.
|
|
||||||
|
|
||||||
If `overwrite` is True (the default), any knot values with precisely
|
|
||||||
the same float32 representation will be overwritten by the incoming
|
|
||||||
values. If not, a KeyError will be raised. Counting on this is not
|
|
||||||
recommended, due to the vagaries of floating-point representations,
|
|
||||||
but it works fine in a pinch.
|
|
||||||
|
|
||||||
Endpoint-preservation is not guaranteed (or conversely, is guaranteed
|
|
||||||
not to work) if any time is passed outside of the exclusive range
|
|
||||||
(0,1).
|
|
||||||
"""
|
|
||||||
old = dict(self.knots.T[1:-1])
|
|
||||||
new = dict(knots)
|
|
||||||
if not overwrite and set(old).intersection(set(new)):
|
|
||||||
raise KeyError("Conflicting spline times")
|
|
||||||
old.update(new)
|
|
||||||
knots = list(sum(sorted(old.items()), ()))
|
|
||||||
self.knots = self.normalize(knots, self(0, 1), self(1, 1))
|
|
||||||
|
|
||||||
def insert_knot(self, t, v):
|
|
||||||
self.update([(t, v)], True)
|
|
||||||
|
|
||||||
def palette_decode(datastrs):
|
|
||||||
"""
|
|
||||||
Decode a palette (stored as a list suitable for JSON packing) into a
|
|
||||||
palette. Internal palette format is simply as a (256,4) array of [0,1]
|
|
||||||
RGBA floats.
|
|
||||||
"""
|
|
||||||
if datastrs[0] != 'rgb8':
|
|
||||||
raise NotImplementedError
|
|
||||||
raw = base64.b64decode(''.join(datastrs[1:]))
|
|
||||||
pal = np.reshape(np.fromstring(raw, np.uint8), (256, 3))
|
|
||||||
data = np.ones((256, 4), np.float32)
|
|
||||||
data[:,:3] = pal / 255.0
|
|
||||||
return data
|
|
||||||
|
|
||||||
def palette_encode(data, format='rgb8'):
|
|
||||||
"""
|
|
||||||
Encode an internal-format palette to an external representation.
|
|
||||||
"""
|
|
||||||
if format != 'rgb8':
|
|
||||||
raise NotImplementedError
|
|
||||||
clamp = np.maximum(0, np.minimum(255, np.round(data[:,:3]*255.0)))
|
|
||||||
enc = base64.b64encode(np.uint8(clamp))
|
|
||||||
return ['rgb8'] + [enc[i:i+64] for i in range(0, len(enc), 64)]
|
|
||||||
|
|
||||||
class _AttrDict(dict):
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name in self:
|
|
||||||
return self[name]
|
|
||||||
raise AttributeError('%s not a dict key' % name)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _wrap(cls, dct):
|
|
||||||
for k, v in dct.items():
|
|
||||||
if (isinstance(v, (float, int)) or
|
|
||||||
(isinstance(v, list) and isinstance(v[1], (float, int)))):
|
|
||||||
dct[k] = SplEval(v)
|
|
||||||
elif isinstance(v, dict):
|
|
||||||
dct[k] = cls._wrap(cls(v))
|
|
||||||
return dct
|
|
||||||
|
|
||||||
class Genome(_AttrDict):
|
|
||||||
"""
|
|
||||||
Load a genome description, wrapping all data structures in _AttrDicts,
|
|
||||||
converting lists of numbers to splines, and deriving some values. Derived
|
|
||||||
values are stored as instance properties, rather than replacing the
|
|
||||||
original values, such that JSON-encoding this structure should always
|
|
||||||
print a valid genome.
|
|
||||||
"""
|
|
||||||
# For now, we base the Genome class on an _AttrDict, letting its structure
|
|
||||||
# be defined implicitly by the way it is used in device code, except for
|
|
||||||
# these derived properties.
|
|
||||||
def __init__(self, gnm):
|
|
||||||
super(Genome, self).__init__(gnm)
|
|
||||||
for k, v in self.items():
|
|
||||||
if not isinstance(v, dict):
|
|
||||||
continue
|
|
||||||
v = _AttrDict(v)
|
|
||||||
# These two properties must be handled separately
|
|
||||||
if k not in ('info', 'time'):
|
|
||||||
_AttrDict._wrap(v)
|
|
||||||
self[k] = v
|
|
||||||
|
|
||||||
self.decoded_palettes = map(palette_decode, self.palettes)
|
|
||||||
pal = self.color.palette_times
|
|
||||||
if isinstance(pal, basestring):
|
|
||||||
self.palette_times = [(0.0, int(pal)), (1.0, int(pal))]
|
|
||||||
else:
|
|
||||||
self.palette_times = zip(pal[::2], map(int, pal[1::2]))
|
|
||||||
|
|
||||||
self.adj_frame_width, self.spp = None, None
|
|
||||||
|
|
||||||
def set_profile(self, prof, offset=0.0, err_spread=True):
|
|
||||||
"""
|
|
||||||
Sets the instance props which are dependent on a profile. Also
|
|
||||||
calculates timing information, which is returned instead of being
|
|
||||||
attached to the genome. May be called multiple times to set different
|
|
||||||
options.
|
|
||||||
|
|
||||||
``prof`` is a profile dictionary. ``offset`` is the time in seconds
|
|
||||||
that the first frame's effective presentation time should be offset
|
|
||||||
from the natural presentation time. ``err_spread`` will spread the
|
|
||||||
rounding error in this frame across all frames, such that PTS+(1/FPS)
|
|
||||||
is exactly equal to the requested duration.
|
|
||||||
|
|
||||||
Returns ``(err, times)``, where ``err`` is the rounding error in
|
|
||||||
seconds (taking ``offset`` into account), and ``times`` is a list of
|
|
||||||
the central time of each frame in the animation in relative-time
|
|
||||||
coordinates. Also sets the ``spp`` and ``adj_frame_width`` properties.
|
|
||||||
"""
|
|
||||||
self.spp = SplEval(self.camera.density.knotlist)
|
|
||||||
self.spp.knots[1] *= prof['quality']
|
|
||||||
fps, base_dur = prof['fps'], prof['duration']
|
|
||||||
|
|
||||||
# TODO: test!
|
|
||||||
dur = self.time.duration
|
|
||||||
if isinstance(dur, basestring):
|
|
||||||
clock = float(dur[:-1]) + offset
|
|
||||||
else:
|
|
||||||
clock = dur * base_dur + offset
|
|
||||||
if (not isinstance(self.get('link'), dict) or
|
|
||||||
not self.link.get('right')):
|
|
||||||
warnings.warn("Genomes with missing or string-valued 'link' "
|
|
||||||
"properties are deprecated, and will be axed shortly.")
|
|
||||||
nframes = int(np.ceil(clock * fps))
|
|
||||||
elif self.link.right == 'reference':
|
|
||||||
nframes = int(np.floor(clock * fps))
|
|
||||||
else:
|
|
||||||
nframes = int(np.ceil(clock * fps))
|
|
||||||
err = (clock - nframes / fps) / clock
|
|
||||||
|
|
||||||
fw = self.time.frame_width
|
|
||||||
if not isinstance(fw, list):
|
|
||||||
fw = [0, fw, 1, fw]
|
|
||||||
fw = [float(f[:-1]) * fps if isinstance(f, basestring)
|
|
||||||
else float(f) / (clock * fps) for f in fw]
|
|
||||||
self.adj_frame_width = SplEval(fw)
|
|
||||||
|
|
||||||
times = np.linspace(offset, 1 - err, nframes + 1)
|
|
||||||
# Move each time to a center time, and discard the last value
|
|
||||||
times = times[:-1] + 0.5 * (times[1] - times[0])
|
|
||||||
if err_spread:
|
|
||||||
epts = np.linspace(-2*np.pi, 2*np.pi, nframes)
|
|
||||||
times = times + 0.5 * err * (np.tanh(epts) + 1)
|
|
||||||
return err, times
|
|
||||||
|
|
||||||
def json_encode_genome(obj):
|
|
||||||
"""
|
|
||||||
Encode an object into JSON notation. This serializer only works on the
|
|
||||||
subset of JSON used in genomes.
|
|
||||||
"""
|
|
||||||
result = _js_enc_obj(obj).lstrip()
|
|
||||||
result = '\n'.join(l.rstrip() for l in result.split('\n'))
|
|
||||||
return result + '\n'
|
|
||||||
|
|
||||||
def _js_enc_obj(obj, indent=0):
|
|
||||||
isnum = lambda v: isinstance(v, (float, int, np.number))
|
|
||||||
|
|
||||||
def wrap(pairs, delims):
|
|
||||||
do, dc = delims
|
|
||||||
i = ' ' * indent
|
|
||||||
out = ''.join([do, ', '.join(pairs), dc])
|
|
||||||
if '\n' not in out and len(out) + indent < 70:
|
|
||||||
return out
|
|
||||||
return ''.join(['\n', i, do, ' ', ('\n'+i+', ').join(pairs),
|
|
||||||
'\n', i, dc])
|
|
||||||
|
|
||||||
if isinstance(obj, dict):
|
|
||||||
if not obj:
|
|
||||||
return '{}'
|
|
||||||
digsort = lambda kv: (int(kv[0]), kv[1]) if kv[0].isdigit() else kv
|
|
||||||
ks, vs = zip(*sorted(obj.items(), key=digsort))
|
|
||||||
if ks == ('b', 'g', 'r'):
|
|
||||||
ks, vs = reversed(ks), reversed(vs)
|
|
||||||
ks = [crep('%.6g' % k if isnum(k) else str(k)) for k in ks]
|
|
||||||
vs = [_js_enc_obj(v, indent+2) for v in vs]
|
|
||||||
return wrap(['%s: %s' % p for p in zip(ks, vs)], '{}')
|
|
||||||
elif isinstance(obj, list):
|
|
||||||
vs = [_js_enc_obj(v, indent+2) for v in obj]
|
|
||||||
if vs and len(vs) % 2 == 0 and isnum(obj[0]):
|
|
||||||
vs = map(', '.join, zip(vs[::2], vs[1::2]))
|
|
||||||
return wrap(vs, '[]')
|
|
||||||
elif isinstance(obj, SplEval):
|
|
||||||
return _js_enc_obj(obj.knotlist, indent)
|
|
||||||
elif isinstance(obj, basestring):
|
|
||||||
return crep(obj)
|
|
||||||
elif isnum(obj):
|
|
||||||
return '%.6g' % obj
|
|
||||||
raise TypeError("Don't know how to serialize %s of type %s" %
|
|
||||||
(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 start_element(self, name, attrs):
|
|
||||||
if name == 'flame':
|
|
||||||
assert self._flame is None
|
|
||||||
self._flame = dict(attrs)
|
|
||||||
self._flame['xforms'] = []
|
|
||||||
self._flame['palette'] = np.ones((256, 4), dtype=np.float32)
|
|
||||||
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][:3] = [float(v) / 255.0
|
|
||||||
for v in attrs['rgb'].split()]
|
|
||||||
elif name == 'symmetry':
|
|
||||||
self._flame['symmetry'] = int(attrs['kind'])
|
|
||||||
def end_element(self, name):
|
|
||||||
if name == 'flame':
|
|
||||||
self.flames.append(self._flame)
|
|
||||||
self._flame = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse(cls, src):
|
|
||||||
parser = cls()
|
|
||||||
parser.parser.Parse(src, True)
|
|
||||||
return parser.flames
|
|
||||||
|
|
||||||
def convert_flame(flame, arc=-360, offset=0):
|
|
||||||
"""
|
|
||||||
Convert an XML flame (as returned by XMLGenomeParser) into a plain dict
|
|
||||||
in cuburn's JSON genome format representing a loop edge. Caller is
|
|
||||||
responsible for correctly setting the 'link' dict.
|
|
||||||
"""
|
|
||||||
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.get('rotate', 0)),
|
|
||||||
'density': 1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
info = {}
|
|
||||||
if 'name' in flame:
|
|
||||||
info['name'] = flame['name']
|
|
||||||
if 'nick' in flame:
|
|
||||||
info['authors'] = [flame['nick']]
|
|
||||||
if flame.get('url'):
|
|
||||||
info['authors'][0] = info['authors'][0] + ', http://' + flame['url']
|
|
||||||
|
|
||||||
time = dict(frame_width=float(flame.get('temporal_filter_width', 1)),
|
|
||||||
duration=abs(arc)/360.)
|
|
||||||
|
|
||||||
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.get('vibrancy', 1))
|
|
||||||
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)])
|
|
||||||
|
|
||||||
num_xf = len(flame['xforms'])
|
|
||||||
xfs = dict([(str(k), convert_xform(v, num_xf, arc, offset))
|
|
||||||
for k, v in enumerate(flame['xforms'])])
|
|
||||||
if 'symmetry' in flame:
|
|
||||||
xfs.update(make_symm_xforms(flame['symmetry'], len(xfs)))
|
|
||||||
if 'finalxform' in flame:
|
|
||||||
xfs['final'] = convert_xform(flame['finalxform'], num_xf,
|
|
||||||
arc, offset, True)
|
|
||||||
return dict(camera=camera, color=color, de=de, xforms=xfs,
|
|
||||||
info=info, time=time, palettes=[pal], link='self')
|
|
||||||
|
|
||||||
def convert_xform(xf, num_xf, arc, offset, 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'), arc, offset, anim)
|
|
||||||
if 'post' in xf and map(float, xf['post'].split()) != [1, 0, 0, 1, 0, 0]:
|
|
||||||
out['post'] = convert_affine(xf.pop('post'), arc, offset)
|
|
||||||
if 'chaos' in xf:
|
|
||||||
chaos = map(float, xf.pop('chaos').split())
|
|
||||||
out['chaos'] = dict()
|
|
||||||
for i in range(num_xf):
|
|
||||||
if i < len(chaos):
|
|
||||||
out['chaos'][str(i)] = chaos[i]
|
|
||||||
else:
|
|
||||||
out['chaos'][str(i)] = 1.0
|
|
||||||
|
|
||||||
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: ' + str(xf)
|
|
||||||
return out
|
|
||||||
|
|
||||||
def convert_affine(aff, arc, offset, 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 + offset, 1, angle + arc + offset]
|
|
||||||
|
|
||||||
return dict(spread=spread, magnitude={'x': xm, 'y': ym},
|
|
||||||
angle=angle, offset={'x': xo, 'y': yo})
|
|
||||||
|
|
||||||
def make_symm_xforms(kind, offset):
|
|
||||||
assert kind != 0, 'Pick your own damn symmetry.'
|
|
||||||
out = []
|
|
||||||
boring_xf = dict(color=1, color_speed=0, density=1, opacity=1,
|
|
||||||
variations={'linear': {'weight': 1}})
|
|
||||||
if kind < 0:
|
|
||||||
out.append(boring_xf.copy())
|
|
||||||
out[-1]['affine'] = dict(angle=135, magnitude={'x': 1, 'y': 1},
|
|
||||||
spread=-45, offset={'x': 0, 'y': 0})
|
|
||||||
kind = -kind
|
|
||||||
for i in range(1, kind):
|
|
||||||
out.append(boring_xf.copy())
|
|
||||||
if kind >= 3:
|
|
||||||
out[-1]['color'] = (i - 1) / (kind - 2.0)
|
|
||||||
ang = (45 + 360 * i / float(kind)) % 360
|
|
||||||
out[-1]['affine'] = dict(angle=ang, magnitude={'x': 1, 'y': 1},
|
|
||||||
spread=-45, offset={'x': 0, 'y': 0})
|
|
||||||
return dict((str(i+offset), v) for i, v in enumerate(out))
|
|
||||||
|
|
||||||
def convert_file(path):
|
|
||||||
"""Quick one-shot conversion for an XML genome."""
|
|
||||||
flames = XMLGenomeParser.parse(open(path).read())
|
|
||||||
if len(flames) > 10:
|
|
||||||
warnings.warn("Lot of flames in this file. Sure it's not a "
|
|
||||||
"frame-based animation?")
|
|
||||||
for flame in flames:
|
|
||||||
yield convert_flame(flame)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import sys
|
|
||||||
print '\n\n'.join(map(json_encode_genome, convert_file(sys.argv[1])))
|
|
0
cuburn/genome/__init__.py
Normal file
0
cuburn/genome/__init__.py
Normal file
288
cuburn/genome/blend.py
Normal file
288
cuburn/genome/blend.py
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# Copyright 2011-2012 Erik Reckase <e.reckase@gmail.com>,
|
||||||
|
# Steven Robertson <steven@strobe.cc>.
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from copy import deepcopy
|
||||||
|
from itertools import izip_longest
|
||||||
|
from scipy.ndimage.filters import gaussian_filter1d
|
||||||
|
|
||||||
|
import spectypes
|
||||||
|
import spec
|
||||||
|
import util
|
||||||
|
from util import get
|
||||||
|
import variations
|
||||||
|
|
||||||
|
# TODO: move to better place before checkin
|
||||||
|
default_blend_opts = {'nloops': 2, 'duration': 2, 'xform_sort': 'weightflip'}
|
||||||
|
|
||||||
|
|
||||||
|
def blend(src, dst, edit={}):
|
||||||
|
"""
|
||||||
|
Blend two nodes to produce an animation.
|
||||||
|
|
||||||
|
``src`` and ``dst`` are the source and destination node specs for the
|
||||||
|
animation. These should be plain node dicts (hierarchical, pre-merged,
|
||||||
|
and adjusted for loop temporal offset).
|
||||||
|
|
||||||
|
``edit`` is an optional edit dict, also hierarchical and pre-merged.
|
||||||
|
|
||||||
|
Returns the animation spec as a plain dict.
|
||||||
|
"""
|
||||||
|
# By design, the blend element will contain only scalar values (no
|
||||||
|
# splines or hierarchy), so this can be done blindly
|
||||||
|
opts = dict(default_blend_opts)
|
||||||
|
for d in src, dst, edit:
|
||||||
|
opts.update(d.get('blend', {}))
|
||||||
|
|
||||||
|
blended = merge_nodes(spec.node, src, dst, edit, opts['nloops'])
|
||||||
|
name_map = sort_xforms(src['xforms'], dst['xforms'], opts['xform_sort'],
|
||||||
|
explicit=zip(*opts.get('xform_map', [])))
|
||||||
|
|
||||||
|
blended['xforms'] = {}
|
||||||
|
for (sxf_key, dxf_key) in name_map:
|
||||||
|
bxf_key = (sxf_key or 'pad') + '_' + (dxf_key or 'pad')
|
||||||
|
xf_edits = merge_edits(spec.xform,
|
||||||
|
get(edit, {}, 'xforms', 'src', sxf_key),
|
||||||
|
get(edit, {}, 'xforms', 'dst', dxf_key))
|
||||||
|
blended['xforms'][bxf_key] = blend_xform(
|
||||||
|
src['xforms'].get(sxf_key),
|
||||||
|
dst['xforms'].get(dxf_key),
|
||||||
|
xf_edits, opts['nloops'])
|
||||||
|
|
||||||
|
if 'final_xform' in src or 'final_xform' in dst:
|
||||||
|
blended['final_xform'] = blend_xform(src.get('final_xform'),
|
||||||
|
dst.get('final_xform'), edit.get('final_xform'), 0, True)
|
||||||
|
|
||||||
|
# TODO: write 'info' section
|
||||||
|
# TODO: palflip
|
||||||
|
blended['type'] = 'animation'
|
||||||
|
blended.setdefault('time', {})['duration'] = opts['duration']
|
||||||
|
return blended
|
||||||
|
|
||||||
|
def merge_edits(sv, av, bv):
|
||||||
|
"""
|
||||||
|
Merge the values of ``av`` and ``bv`` according to the spec ``sv``.
|
||||||
|
"""
|
||||||
|
if isinstance(spec, (dict, spectypes.Map)):
|
||||||
|
av, bv = av or {}, bv or {}
|
||||||
|
getsv = lambda k: sv.type if isinstance(sv, spectypes.Map) else sv[k]
|
||||||
|
return dict([(k, merge_edits(getsv(k), av.get(k), bv.get(k)))
|
||||||
|
for k in set(av.keys() + bv.keys())])
|
||||||
|
elif isinstance(sv, (spectypes.List, spectypes.Spline)):
|
||||||
|
return (av or []) + (bv or [])
|
||||||
|
else:
|
||||||
|
return bv if bv is not None else av
|
||||||
|
|
||||||
|
def tospline(spl, src, dst, edit, loops):
|
||||||
|
def split_node_val(val):
|
||||||
|
if val is None:
|
||||||
|
return spl.default, 0
|
||||||
|
if isinstance(val, (int, float)):
|
||||||
|
return val, 0
|
||||||
|
return val
|
||||||
|
|
||||||
|
sp, sv = split_node_val(src) # position, velocity
|
||||||
|
dp, dv = split_node_val(dst)
|
||||||
|
|
||||||
|
# For variation parameters, copy missing values instead of using defaults
|
||||||
|
if spl.var:
|
||||||
|
if src is None:
|
||||||
|
sp = dp
|
||||||
|
if dst is None:
|
||||||
|
dp = sp
|
||||||
|
|
||||||
|
edit = dict(zip(edit[::2], edit[1::2])) if edit else {}
|
||||||
|
e0, e1 = edit.pop(0, None), edit.pop(1, None)
|
||||||
|
edit = zip(*[(k, v) for k, v in edit.items() if v is not None])
|
||||||
|
|
||||||
|
if spl.period:
|
||||||
|
# Periodic extension: compute an appropriate number of loops based on
|
||||||
|
# the angular velocities at the endpoints, and extend the destination
|
||||||
|
# position by the appropriate number of periods.
|
||||||
|
avg_vel = round(float(sv + dv) * loops / spl.period)
|
||||||
|
dp = dp % spl.period + avg_vel * spl.period
|
||||||
|
|
||||||
|
# Endpoint override: allow adjusting the number of loops as calculated
|
||||||
|
# above by locking to the nearest value with the same mod (i.e. the
|
||||||
|
# nearest value which will still line up with the node)
|
||||||
|
if e0 is not None:
|
||||||
|
sp += round(float(e0 - sp) / spl.period) * spl.period
|
||||||
|
if e1 is not None:
|
||||||
|
dp += round(float(e1 - dp) / spl.period) * spl.period
|
||||||
|
if edit or sv or dv:
|
||||||
|
return [sp, sv, dp, dv] + edit
|
||||||
|
if sp != dp:
|
||||||
|
return [sp, dp]
|
||||||
|
return sp
|
||||||
|
|
||||||
|
def trace(k):
|
||||||
|
print k,
|
||||||
|
return k
|
||||||
|
|
||||||
|
def merge_nodes(sp, src, dst, edit, loops):
|
||||||
|
if isinstance(sp, dict):
|
||||||
|
src, dst, edit = [x or {} for x in src, dst, edit]
|
||||||
|
return dict([(k, merge_nodes(sp[k], src.get(k),
|
||||||
|
dst.get(k), edit.get(k), loops))
|
||||||
|
for k in set(src.keys() + dst.keys() + edit.keys()) if k in sp])
|
||||||
|
elif isinstance(sp, spectypes.Spline):
|
||||||
|
return tospline(sp, src, dst, edit, loops)
|
||||||
|
elif isinstance(sp, spectypes.List):
|
||||||
|
if isinstance(sp.type, spectypes.Palette):
|
||||||
|
if src is not None: src = [[0] + src]
|
||||||
|
if dst is not None: dst = [[1] + dst]
|
||||||
|
return (src or []) + (dst or []) + (edit or [])
|
||||||
|
else:
|
||||||
|
return edit if edit is not None else dst if dst is not None else src
|
||||||
|
|
||||||
|
def blend_xform(sxf, dxf, edits, loops, isfinal=False):
|
||||||
|
if sxf is None:
|
||||||
|
sxf = padding_xform(dxf, isfinal)
|
||||||
|
if dxf is None:
|
||||||
|
dxf = padding_xform(sxf, isfinal)
|
||||||
|
return merge_nodes(spec.xform, sxf, dxf, edits, loops)
|
||||||
|
|
||||||
|
# If xin contains any of these, use the inverse identity
|
||||||
|
hole_variations = ('spherical ngon julian juliascope polar '
|
||||||
|
'wedge_sph wedge_julia bipolar').split()
|
||||||
|
|
||||||
|
# These variations are identity functions at their default values
|
||||||
|
ident_variations = ('rectangles rings2 fan2 blob perspective curl '
|
||||||
|
'super_shape').split()
|
||||||
|
|
||||||
|
def padding_xform(xf, isfinal):
|
||||||
|
vars = {}
|
||||||
|
xout = {'variations': vars, 'pre_affine': {'angle': 45}}
|
||||||
|
if isfinal:
|
||||||
|
xout.update(weight=0, color_speed=0)
|
||||||
|
if get(xf, 45, 'pre_affine', 'spread') > 90:
|
||||||
|
xout['pre_affine'] = {'angle': 135, 'spread': 135}
|
||||||
|
if get(xf, 45, 'post_affine', 'spread') > 90:
|
||||||
|
xout['post_affine'] = {'angle': 135, 'spread': 135}
|
||||||
|
|
||||||
|
for k in xf['variations']:
|
||||||
|
if k in hole_variations:
|
||||||
|
# Attempt to correct for some known-ugly variations.
|
||||||
|
xout['pre_affine']['angle'] += 180
|
||||||
|
vars['linear'] = dict(weight=-1)
|
||||||
|
return xout
|
||||||
|
if k in ident_variations:
|
||||||
|
# Try to use non-linear variations whenever we can
|
||||||
|
vars[k] = dict([(vk, vv.default)
|
||||||
|
for vk, vv in variations.var_params[k].items()])
|
||||||
|
|
||||||
|
if vars:
|
||||||
|
n = float(len(vars))
|
||||||
|
for k in vars:
|
||||||
|
vars[k]['weight'] /= n
|
||||||
|
else:
|
||||||
|
vars['linear'] = dict(weight=1)
|
||||||
|
|
||||||
|
return xout
|
||||||
|
|
||||||
|
def blend_genomes(left, right, nloops=2, align='weightflip', seed=None,
|
||||||
|
stagger=False, blur=None, palflip=True):
|
||||||
|
align_xforms(left, right, align)
|
||||||
|
name = '%s=%s' % (left.info.get('name', ''), right.info.get('name', ''))
|
||||||
|
if seed is None:
|
||||||
|
seed = map(ord, name)
|
||||||
|
rng = np.random.RandomState(seed)
|
||||||
|
|
||||||
|
blend = blend_splines(left, right, nloops, rng, stagger)
|
||||||
|
# TODO: licenses; check license compatibility when merging
|
||||||
|
# TODO: add URL and flockutil revision to authors
|
||||||
|
blend['info'] = {
|
||||||
|
'name': name,
|
||||||
|
'authors': sum([g.info.get('authors', []) for g in left, right], [])
|
||||||
|
}
|
||||||
|
blend['info']['authors'].append('flockutil')
|
||||||
|
blend['palettes'] = [get_palette(left, False), get_palette(right, True)]
|
||||||
|
blend['color']['palette_times'] = [0, "0", 1, "1"]
|
||||||
|
|
||||||
|
if palflip:
|
||||||
|
checkpalflip(blend)
|
||||||
|
|
||||||
|
if blur:
|
||||||
|
blur_palettes(blend, blur)
|
||||||
|
|
||||||
|
return blend
|
||||||
|
|
||||||
|
|
||||||
|
def halfhearted_human_sort_key(key):
|
||||||
|
try:
|
||||||
|
return int(key)
|
||||||
|
except ValueError:
|
||||||
|
return key
|
||||||
|
|
||||||
|
def sort_xforms(sxfs, dxfs, sortmethod, explicit=[]):
|
||||||
|
# Walk through the explicit pairs, popping previous matches from the
|
||||||
|
# forward (src=>dst) and reverse (dst=>src) maps
|
||||||
|
fwd, rev = {}, {}
|
||||||
|
for sx, dx in explicit:
|
||||||
|
if sx in fwd:
|
||||||
|
rev.pop(fwd.pop(sx, None), None)
|
||||||
|
if dx in rev:
|
||||||
|
fwd.pop(rev.pop(dx, None), None)
|
||||||
|
fwd[sx] = dx
|
||||||
|
rev[dx] = sx
|
||||||
|
|
||||||
|
for sd in sorted(fwd.items()):
|
||||||
|
yield sd
|
||||||
|
|
||||||
|
# Classify the remaining xforms. Currently we classify based on whether
|
||||||
|
# the pre- and post-affine transforms are flipped
|
||||||
|
scl, dcl = {}, {}
|
||||||
|
for (cl, xfs, exp) in [(scl, sxfs, fwd), (dcl, dxfs, rev)]:
|
||||||
|
for k, v in xfs.items():
|
||||||
|
if k in exp: continue
|
||||||
|
xcl = (get(v, 45, 'pre_affine', 'spread') > 90,
|
||||||
|
get(v, 45, 'post_affine', 'spread') > 90)
|
||||||
|
cl.setdefault(xcl, []).append(k)
|
||||||
|
|
||||||
|
def sort(keys, dct, snd=False):
|
||||||
|
if sortmethod in ('weight', 'weightflip'):
|
||||||
|
sortf = lambda k: dct[k].get('weight', 0)
|
||||||
|
elif sortmethod == 'color':
|
||||||
|
sortf = lambda k: dct[k].get('color', 0)
|
||||||
|
else:
|
||||||
|
# 'natural' key-based sort
|
||||||
|
sortf = halfhearted_human_sort_key
|
||||||
|
return sorted(keys, key=sortf)
|
||||||
|
|
||||||
|
for cl in set(scl.keys() + dcl.keys()):
|
||||||
|
ssort = sort(scl.get(cl, []), sxfs)
|
||||||
|
dsort = sort(dcl.get(cl, []), dxfs)
|
||||||
|
if sortmethod == 'weightflip':
|
||||||
|
dsort = reversed(dsort)
|
||||||
|
for sd in izip_longest(ssort, dsort):
|
||||||
|
yield sd
|
||||||
|
|
||||||
|
def checkpalflip(gnm):
|
||||||
|
if 'final' in gnm['xforms']:
|
||||||
|
f = gnm['xforms']['final']
|
||||||
|
fcv, fcsp = f['color'], f['color_speed']
|
||||||
|
else:
|
||||||
|
fcv, fcsp = SplEval(0), SplEval(0)
|
||||||
|
sansfinal = [v for k, v in gnm['xforms'].items() if k != 'final']
|
||||||
|
|
||||||
|
lc, rc = [np.array([v['color'](t) * (1 - fcsp(t)) + fcv(t) * fcsp(t)
|
||||||
|
for v in sansfinal]) for t in (0, 1)]
|
||||||
|
rcrv = 1 - rc
|
||||||
|
# TODO: use spline integration instead of L2
|
||||||
|
dens = np.array([np.hypot(v['weight'](0), v['weight'](1))
|
||||||
|
for v in sansfinal])
|
||||||
|
return np.sum(np.abs(dens * (rc - lc))) > np.sum(np.abs(dens * (rcrv - lc)))
|
||||||
|
|
||||||
|
def palflip(gnm):
|
||||||
|
for v in gnm['xforms'].values():
|
||||||
|
c = v['color']
|
||||||
|
v['color'] = SplEval([0, c(0), 1, 1 - c(1)], c(0, 1), -c(1, 1))
|
||||||
|
pal = genome.palette_decode(gnm['palettes'][1])
|
||||||
|
gnm['palettes'][1] = genome.palette_encode(np.flipud(pal))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys, json
|
||||||
|
a, b, c = [json.load(open(f+'.json')) for f in 'abc']
|
||||||
|
print util.json_encode(blend(a, b, c))
|
182
cuburn/genome/convert.py
Normal file
182
cuburn/genome/convert.py
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import warnings
|
||||||
|
import xml.parsers.expat
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from variations import var_params
|
||||||
|
import util
|
||||||
|
|
||||||
|
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 start_element(self, name, attrs):
|
||||||
|
if name == 'flame':
|
||||||
|
assert self._flame is None
|
||||||
|
self._flame = dict(attrs)
|
||||||
|
self._flame['xforms'] = []
|
||||||
|
self._flame['palette'] = np.ones((256, 4), dtype=np.float32)
|
||||||
|
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][:3] = [float(v) / 255.0
|
||||||
|
for v in attrs['rgb'].split()]
|
||||||
|
elif name == 'symmetry':
|
||||||
|
self._flame['symmetry'] = int(attrs['kind'])
|
||||||
|
def end_element(self, name):
|
||||||
|
if name == 'flame':
|
||||||
|
self.flames.append(self._flame)
|
||||||
|
self._flame = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, src):
|
||||||
|
parser = cls()
|
||||||
|
parser.parser.Parse(src, True)
|
||||||
|
return parser.flames
|
||||||
|
|
||||||
|
def convert_affine(aff, animate=False):
|
||||||
|
xx, yx, xy, yy, xo, yo = vals = map(float, aff.split())
|
||||||
|
if vals == [1, 0, 0, 1, 0, 0]: return None
|
||||||
|
|
||||||
|
# Cuburn's IFS-space vertical direction is inverted with respect to flam3,
|
||||||
|
# so we invert all instances of y. (``yy`` is effectively 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)
|
||||||
|
spread = ((ya - xa) % 360) / 2
|
||||||
|
angle = (xa + spread) % 360
|
||||||
|
return dict(spread=spread, magnitude={'x': xm, 'y': ym},
|
||||||
|
angle=angle, offset={'x': xo, 'y': yo})
|
||||||
|
|
||||||
|
def convert_vars(xf):
|
||||||
|
struct = lambda k, ps: ([('weight', k, float)] +
|
||||||
|
[(p, k+'_'+p, float) for p in ps])
|
||||||
|
return dict([(k, apply_structure(struct(k, ps), xf))
|
||||||
|
for k, ps in var_params.items() if k in xf])
|
||||||
|
|
||||||
|
def convert_xform(xf):
|
||||||
|
out = apply_structure(xform_structure, xf)
|
||||||
|
|
||||||
|
# Deprecated symmetry arg makes this too much of a bother to handle within
|
||||||
|
# the structure framework
|
||||||
|
symm = float(xf.get('symmetry', 0))
|
||||||
|
anim = xf.get('animate', symm <= 0)
|
||||||
|
if 'symmetry' in xf:
|
||||||
|
out.setdefault('color_speed', (1-symm)/2)
|
||||||
|
if anim and 'pre_affine' in out:
|
||||||
|
out['pre_affine']['angle'] = [out['pre_affine']['angle'], -360]
|
||||||
|
return out
|
||||||
|
|
||||||
|
def make_symm_xforms(kind, offset):
|
||||||
|
assert kind != 0, 'Pick your own damn symmetry.'
|
||||||
|
out = []
|
||||||
|
boring_xf = dict(color=1, color_speed=0, density=1,
|
||||||
|
variations={'linear': {'weight': 1}})
|
||||||
|
if kind < 0:
|
||||||
|
out.append(boring_xf.copy())
|
||||||
|
out[-1]['affine'] = dict(angle=135, spread=-45)
|
||||||
|
kind = -kind
|
||||||
|
for i in range(1, kind):
|
||||||
|
out.append(boring_xf.copy())
|
||||||
|
if kind >= 3:
|
||||||
|
out[-1]['color'] = (i - 1) / (kind - 2.0)
|
||||||
|
ang = (45 + 360 * i / float(kind)) % 360
|
||||||
|
out[-1]['affine'] = dict(angle=ang, spread=-45)
|
||||||
|
return dict(enumerate(out, offset))
|
||||||
|
|
||||||
|
def convert_xforms(flame):
|
||||||
|
xfs = dict(enumerate(map(convert_xform, flame['xforms'])))
|
||||||
|
if 'symmetry' in flame:
|
||||||
|
xfs.update(make_symm_xforms(float(flame['symmetry']), len(xfs)))
|
||||||
|
return xfs
|
||||||
|
|
||||||
|
split_to_dict = lambda keys: lambda v: dict(zip(keys, map(float, v.split())))
|
||||||
|
pair = split_to_dict('xy')
|
||||||
|
rgb_triple = split_to_dict('rgb')
|
||||||
|
|
||||||
|
xform_structure = (
|
||||||
|
('pre_affine', 'coefs', convert_affine),
|
||||||
|
('post_affine', 'post', convert_affine),
|
||||||
|
('color', 'color', float),
|
||||||
|
('color_speed', 'color_speed', float),
|
||||||
|
('opacity', 'opacity', float),
|
||||||
|
('weight', 'weight', float),
|
||||||
|
('chaos', 'chaos',
|
||||||
|
lambda s: dict(enumerate(map(float, s.split())))),
|
||||||
|
('variations', convert_vars)
|
||||||
|
)
|
||||||
|
|
||||||
|
# A list of either three-tuples (dst, src, cvt_val), or two-tuples
|
||||||
|
# (dst, cvt_dict) for properties that are built from multiple source keys.
|
||||||
|
# If a function returns 'None', its key is dropped from the result.
|
||||||
|
flame_structure = (
|
||||||
|
('info.author', 'nick', str),
|
||||||
|
('info.author_url', 'url', lambda s: 'http://' + str(s)),
|
||||||
|
('info.name', 'name', str),
|
||||||
|
|
||||||
|
('camera.center', 'center', pair),
|
||||||
|
('camera.rotation', 'rotate', float),
|
||||||
|
('camera.dither_width', 'filter', float),
|
||||||
|
('camera.scale',
|
||||||
|
lambda d: float(d['scale']) / float(d['size'].split()[0])),
|
||||||
|
|
||||||
|
('filters.colorclip.gamma', 'filter', float),
|
||||||
|
('filters.colorclip.gamma_threshold', 'gamma_threshold', float),
|
||||||
|
('filters.colorclip.highlight_power', 'highlight_power', float),
|
||||||
|
('filters.colorclip.vibrance', 'vibrancy', float),
|
||||||
|
# Not sure about putting this one on colorclip
|
||||||
|
('filters.colorclip.background', 'background', rgb_triple),
|
||||||
|
|
||||||
|
('filters.de.curve', 'estimator_curve', float),
|
||||||
|
('filters.de.radius', 'estimator_radius', float),
|
||||||
|
('filters.de.minimum',
|
||||||
|
lambda d: (float(d['estimator_minimum']) /
|
||||||
|
float(d.get('estimator_radius', 11)))
|
||||||
|
if 'estimator_minimum' in d else None),
|
||||||
|
|
||||||
|
('palette', 'palette', util.palette_encode),
|
||||||
|
('xforms', convert_xforms),
|
||||||
|
('final_xform', 'finalxform', convert_xform),
|
||||||
|
)
|
||||||
|
|
||||||
|
def apply_structure(struct, src):
|
||||||
|
out = {}
|
||||||
|
for l in struct:
|
||||||
|
if len(l) == 2:
|
||||||
|
v = l[1](src)
|
||||||
|
else:
|
||||||
|
v = l[2](src[l[1]]) if l[1] in src else None
|
||||||
|
if v is not None:
|
||||||
|
out[l[0]] = v
|
||||||
|
return out
|
||||||
|
|
||||||
|
def convert_flame(flame):
|
||||||
|
return util.unflatten(util.flatten(apply_structure(flame_structure, flame)))
|
||||||
|
|
||||||
|
def convert_file(path):
|
||||||
|
"""Quick one-shot conversion for an XML genome."""
|
||||||
|
flames = XMLGenomeParser.parse(open(path).read())
|
||||||
|
if len(flames) > 10:
|
||||||
|
warnings.warn("Lot of flames in this file. Sure it's not a "
|
||||||
|
"frame-based animation?")
|
||||||
|
for flame in flames:
|
||||||
|
yield convert_flame(flame)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
print '\n\n'.join(map(util.json_encode, convert_file(sys.argv[1])))
|
82
cuburn/genome/schema.py
Normal file
82
cuburn/genome/schema.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
from schematypes import *
|
||||||
|
from variations import var_
|
||||||
|
|
||||||
|
affine = (
|
||||||
|
{ angle: spline(45, period=360)
|
||||||
|
, spread: spline(45, period=180)
|
||||||
|
# TODO: should these scale relative to magnitude?
|
||||||
|
, off_x: spline()
|
||||||
|
, off_y: spline()
|
||||||
|
# TODO: this is probably an inappropriate scaling domain? Should one be
|
||||||
|
# constructed specifically for magnitudes?
|
||||||
|
, mag_x: spline(1)
|
||||||
|
, mag_y: spline(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
xform = (
|
||||||
|
{ affine: affine
|
||||||
|
, post: affine
|
||||||
|
, color: spline(0, 0, 1)
|
||||||
|
, color_speed: spline(0.5, 0, 1)
|
||||||
|
, density: spline()
|
||||||
|
, opacity: scalespline(max=1)
|
||||||
|
, variations: cuburn.code.variations.params
|
||||||
|
})
|
||||||
|
|
||||||
|
# Since the structure of the info element differs between anims, nodes and
|
||||||
|
# edges, we pull out some of the common elements here
|
||||||
|
author = String('Attribution in the form: "Name [<email>][, url]"')
|
||||||
|
name = String('A human-readable name for this entity')
|
||||||
|
src = String('The identifier of the source node')
|
||||||
|
dst = String('The identifier of the destination node')
|
||||||
|
|
||||||
|
filters = (
|
||||||
|
{ bilateral:
|
||||||
|
{ spatial_std: scalespline(d='Scale of profile spatial standard deviation')
|
||||||
|
, color_std: scalespline(d='Scale of profile color standard deviation')
|
||||||
|
, density_std: scalespline(d='Scale of profile density standard deviation')
|
||||||
|
, density_pow: scalespline(d='Scale of profile density pre-blur exponent')
|
||||||
|
, gradient: spline(1, d='Scale of profile gradient filter intensity '
|
||||||
|
'(can be negative)')
|
||||||
|
}
|
||||||
|
, colorclip:
|
||||||
|
{ bg_r: spline(0, 0, 1)
|
||||||
|
, bg_g: spline(0, 0, 1)
|
||||||
|
, bg_b: spline(0, 0, 1)
|
||||||
|
, gamma: scalespline()
|
||||||
|
, gamma_threshold: spline(0.01, 0, 1)
|
||||||
|
, highlight_power: spline(-1, -1, 1)
|
||||||
|
, vibrance: spline(1, 0, 1)
|
||||||
|
}
|
||||||
|
, de:
|
||||||
|
{ radius: scalespline(d='Scale of profile filter radius')
|
||||||
|
, minimum: scalespline(0, d='Scale against adjusted DE radius of '
|
||||||
|
'minimum radius')
|
||||||
|
, curve: scalespline(0.6, d='Absolute (unscaled) value of DE curve')
|
||||||
|
}
|
||||||
|
# TODO: absolute or relative?
|
||||||
|
, logscale: {brightness: scalespline(4, d='Absolute log brightness')}
|
||||||
|
})
|
||||||
|
|
||||||
|
anim = (
|
||||||
|
{ type: 'animation'
|
||||||
|
, info: dict(authors=List(author), name=name, src=src, dst=dst)
|
||||||
|
, camera:
|
||||||
|
# Should center_{xy} be scaled relative to the 'scale' parameter, or is
|
||||||
|
# that just too complicated for this representation?
|
||||||
|
{ center_x: spline()
|
||||||
|
, center_y: spline()
|
||||||
|
, density: scalespline()
|
||||||
|
, dither_width: scalespline()
|
||||||
|
, rotation: spline(period=360)
|
||||||
|
, scale: scalespline()
|
||||||
|
}
|
||||||
|
, filters: filters
|
||||||
|
, time:
|
||||||
|
{ duration:
|
||||||
|
, frame_width: scalespline(d='Scale of profile temporal width per frame.')
|
||||||
|
}
|
||||||
|
, palettes: list_(Palette())
|
||||||
|
, xforms: map(xform)
|
||||||
|
, final_xform: xform
|
||||||
|
})
|
112
cuburn/genome/spec.py
Normal file
112
cuburn/genome/spec.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
from spectypes import *
|
||||||
|
from variations import var_params
|
||||||
|
|
||||||
|
affine = (
|
||||||
|
{ 'angle': spline(45, period=360)
|
||||||
|
, 'spread': spline(45, period=180)
|
||||||
|
, 'magnitude': XYPair(scalespline())
|
||||||
|
, 'offset': XYPair(spline())
|
||||||
|
})
|
||||||
|
|
||||||
|
xform = (
|
||||||
|
{ 'pre_affine': affine
|
||||||
|
, 'post_affine': affine
|
||||||
|
, 'color': spline(0, 0, 1)
|
||||||
|
, 'color_speed': spline(0.5, 0, 1)
|
||||||
|
, 'weight': spline()
|
||||||
|
, 'opacity': scalespline(max=1)
|
||||||
|
, 'variations': var_params
|
||||||
|
})
|
||||||
|
|
||||||
|
# Since the structure of the info element differs between anims, nodes and
|
||||||
|
# edges, we pull out some of the common elements here
|
||||||
|
author = String('Attribution in the form: "Name [<email>][, url]"')
|
||||||
|
name = String('A human-readable name for this entity')
|
||||||
|
src = String('The identifier of the source node')
|
||||||
|
dst = String('The identifier of the destination node')
|
||||||
|
|
||||||
|
filters = (
|
||||||
|
{ 'bilateral':
|
||||||
|
{ 'spatial_std': scalespline(6,
|
||||||
|
d='Spatial filter radius, normalized to 1080p pixels')
|
||||||
|
, 'color_std': scalespline(0.05,
|
||||||
|
d='Color filter radius, in YUV space, normalized to [0,1]')
|
||||||
|
, 'density_std': scalespline(1.5, d='Density standard deviation')
|
||||||
|
, 'density_pow': scalespline(0.8, d='Density pre-filter power')
|
||||||
|
, 'gradient': scalespline(4.0, min=None,
|
||||||
|
d='Intensity of gradient amplification (can be negative)')
|
||||||
|
}
|
||||||
|
, 'colorclip':
|
||||||
|
{ 'gamma': scalespline(4)
|
||||||
|
, 'gamma_threshold': spline(0.01, 0, 1)
|
||||||
|
, 'highlight_power': spline(-1, -1, 1)
|
||||||
|
, 'vibrance': scalespline()
|
||||||
|
}
|
||||||
|
, 'de':
|
||||||
|
{ 'radius': scalespline(11, d='Spatial filter radius in flam3 units')
|
||||||
|
, 'minimum': scalespline(0, max=1, d='Proportional min radius')
|
||||||
|
, 'curve': scalespline(0.6, d='Power of filter radius with density')
|
||||||
|
}
|
||||||
|
, 'haloclip': {'gamma': scalespline(4)}
|
||||||
|
, 'logscale': {'brightness': scalespline(4, d='Log-scale brightness')}
|
||||||
|
})
|
||||||
|
|
||||||
|
camera = (
|
||||||
|
{ 'center': XYPair(spline())
|
||||||
|
, 'spp': scalespline(d='Samples per pixel multiplier')
|
||||||
|
, 'dither_width': scalespline()
|
||||||
|
, 'rotation': spline(period=360)
|
||||||
|
, 'scale': scalespline()
|
||||||
|
})
|
||||||
|
|
||||||
|
time = (
|
||||||
|
{ 'duration': scalar(1)
|
||||||
|
, 'frame_width': scalespline(d='Scale of profile temporal width per frame.')
|
||||||
|
})
|
||||||
|
|
||||||
|
base = (
|
||||||
|
{ 'camera': camera
|
||||||
|
, 'filters': filters
|
||||||
|
, 'palette': list_(Palette())
|
||||||
|
, 'xforms': map_(xform)
|
||||||
|
, 'final_xform': xform
|
||||||
|
})
|
||||||
|
|
||||||
|
anim = dict(base)
|
||||||
|
anim.update(type='animation', time=time,
|
||||||
|
info=dict(authors=list_(author), name=name, src=src, dst=dst,
|
||||||
|
origin=string_()))
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
node = dict(base)
|
||||||
|
node.update(type='node', info=dict(author=author, author_url=string_(),
|
||||||
|
id=string_(), name=name))
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
edge = dict(anim)
|
||||||
|
edge.update(type='edge',
|
||||||
|
info=dict(author=author, id=string_(), src=src, dst=dst),
|
||||||
|
xforms=dict(src=map_(xform), dst=map_(xform)))
|
||||||
|
|
||||||
|
# Yeah, now I'm just messing around.
|
||||||
|
prof_filters = dict([(fk, dict([(k, refscalar(1, '.'.join(['filters', fk, k])))
|
||||||
|
for k in fv])) for fk, fv in filters.items()])
|
||||||
|
# And here's a completely stupid hack to drag scale into the logscale filter
|
||||||
|
prof_filters['logscale']['scale'] = refscalar(1, 'camera.scale')
|
||||||
|
|
||||||
|
default_filters = [{'type': k} for k in ['bilateral', 'logscale', 'colorclip']]
|
||||||
|
|
||||||
|
profile = (
|
||||||
|
{ 'duration': RefScalar(30, 'time.duration', 'Base duration in seconds')
|
||||||
|
, 'fps': Scalar(24, 'Frames per second')
|
||||||
|
, 'height': Scalar(1920, 'Output height in pixels')
|
||||||
|
, 'width': Scalar(1080, 'Output width in pixels')
|
||||||
|
, 'frame_width': refscalar(1, 'time.frame_width')
|
||||||
|
, 'spp': RefScalar(2000, 'camera.spp', 'Base samples per pixel')
|
||||||
|
, 'skip': Scalar(0, 'Skip this many frames between each rendered frame')
|
||||||
|
, 'filters': TypedList(prof_filters, default_filters,
|
||||||
|
'Ordered list of filters to apply')
|
||||||
|
})
|
||||||
|
|
||||||
|
# Types recognized as independent units with a 'type' key
|
||||||
|
toplevels = dict(animation=anim, node=node, edge=edge, profile=profile)
|
45
cuburn/genome/spectypes.py
Normal file
45
cuburn/genome/spectypes.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
Map = namedtuple('Map', 'type doc')
|
||||||
|
map_ = lambda type, d=None: Map(type, d)
|
||||||
|
|
||||||
|
List = namedtuple('List', 'type doc')
|
||||||
|
list_ = lambda type, d=None: List(type, d)
|
||||||
|
|
||||||
|
# A list as above, but where each element is a dict with a 'type' parameter
|
||||||
|
# corresponding to one of the specs listed in the 'types' dict on this spec.
|
||||||
|
TypedList = namedtuple('TypedList', 'types defaults doc')
|
||||||
|
typedlist = lambda types, defaults=[], d=None: TypedList(types, defaults, d)
|
||||||
|
|
||||||
|
Spline = namedtuple('Spline', 'default min max interp period doc var')
|
||||||
|
def spline(default=0, min=None, max=None, interp='linear', period=None, d=None):
|
||||||
|
return Spline(default, min, max, interp, period, d, False)
|
||||||
|
def scalespline(default=1, min=0, max=None, d=None):
|
||||||
|
"""Spline helper, with defaults appropriate for a scaling parameter."""
|
||||||
|
return Spline(default, min, None, 'mag', None, d, False)
|
||||||
|
|
||||||
|
class XYPair(dict):
|
||||||
|
"""
|
||||||
|
Specialization of spline over two dimensions. Separate type is a hint to
|
||||||
|
UIs and mutator, but this may be treated just like a normal dict.
|
||||||
|
"""
|
||||||
|
def __init__(self, type):
|
||||||
|
self['x'] = self['y'] = self.type = type
|
||||||
|
|
||||||
|
Scalar = namedtuple('Scalar', 'default doc')
|
||||||
|
scalar = lambda default, d=None: Scalar(default, d)
|
||||||
|
|
||||||
|
# These are scalars, as used in profiles, but which are scaled by some other
|
||||||
|
# parameter (in the genome) given by name as ``ref``.
|
||||||
|
RefScalar = namedtuple('RefScalar', 'default ref doc')
|
||||||
|
refscalar = lambda default, ref, d=None: RefScalar(default, ref, d)
|
||||||
|
|
||||||
|
String = namedtuple('String', 'doc')
|
||||||
|
def string_(d=None):
|
||||||
|
return String(d)
|
||||||
|
Enum = namedtuple('Enum', 'choices doc')
|
||||||
|
def enum(choices, d=None):
|
||||||
|
"""Enum helper. 'choices' is a space-separated string."""
|
||||||
|
return Enum(choices.split(), d)
|
||||||
|
|
||||||
|
Palette = namedtuple('Palette', '')
|
188
cuburn/genome/use.py
Normal file
188
cuburn/genome/use.py
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from spectypes import Spline, Scalar, RefScalar, Map, List, TypedList
|
||||||
|
from spec import toplevels
|
||||||
|
|
||||||
|
class Wrapper(object):
|
||||||
|
def __init__(self, val, spec=None, path=()):
|
||||||
|
if spec is None:
|
||||||
|
spec = toplevels[val['type']]
|
||||||
|
# plain 'val' would conflict with some variation property names
|
||||||
|
self._val, self.spec, self.path = val, spec, path
|
||||||
|
|
||||||
|
def wrap(self, name, spec, val):
|
||||||
|
# Oh, a visitor. How... pedestrian.
|
||||||
|
path = self.path + (name,)
|
||||||
|
if isinstance(spec, Spline):
|
||||||
|
return self.wrap_spline(path, spec, val)
|
||||||
|
elif isinstance(spec, Scalar):
|
||||||
|
return self.wrap_scalar(path, spec, val)
|
||||||
|
elif isinstance(spec, RefScalar):
|
||||||
|
return self.wrap_refscalar(path, spec, val)
|
||||||
|
elif isinstance(spec, dict):
|
||||||
|
return self.wrap_dict(path, spec, val)
|
||||||
|
elif isinstance(spec, Map):
|
||||||
|
return self.wrap_Map(path, spec, val)
|
||||||
|
elif isinstance(spec, List):
|
||||||
|
return self.wrap_List(path, spec, val)
|
||||||
|
elif isinstance(spec, TypedList):
|
||||||
|
return self.wrap_TypedList(path, spec, val)
|
||||||
|
return self.wrap_default(path, spec, val)
|
||||||
|
|
||||||
|
def wrap_default(self, path, spec, val):
|
||||||
|
return val
|
||||||
|
|
||||||
|
def wrap_spline(self, path, spec, val):
|
||||||
|
return val
|
||||||
|
|
||||||
|
def wrap_scalar(self, path, spec, val):
|
||||||
|
return val if val is not None else spec.default
|
||||||
|
|
||||||
|
def wrap_dict(self, path, spec, val):
|
||||||
|
return type(self)(val or {}, spec, path)
|
||||||
|
|
||||||
|
def wrap_Map(self, path, spec, val):
|
||||||
|
return self.wrap_dict(path, spec, val)
|
||||||
|
|
||||||
|
def wrap_List(self, path, spec, val):
|
||||||
|
return [self.wrap(spec.type, v) for v in val]
|
||||||
|
|
||||||
|
def wrap_TypedList(self, path, spec, val):
|
||||||
|
val = val if val is not None else spec.defaults
|
||||||
|
return [self.wrap(path+(str(i),), spec.types[v['type']], v)
|
||||||
|
for i, v in enumerate(val)]
|
||||||
|
|
||||||
|
def get_spec(self, name):
|
||||||
|
if isinstance(self.spec, Map):
|
||||||
|
return self.spec.type
|
||||||
|
return self.spec[name]
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return self.wrap(name, self.get_spec(name), self._val.get(name))
|
||||||
|
|
||||||
|
# Container emulation
|
||||||
|
def keys(self):
|
||||||
|
return sorted(self._val.keys())
|
||||||
|
def items(self):
|
||||||
|
return sorted((k, self[k]) for k in self)
|
||||||
|
def __contains__(self, name):
|
||||||
|
self.get_spec(name) # raise IndexError if name is not typed
|
||||||
|
return name in self._val
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(sorted(self._val))
|
||||||
|
def __getitem__(self, name):
|
||||||
|
return getattr(self, str(name))
|
||||||
|
|
||||||
|
|
||||||
|
class RefWrapper(Wrapper):
|
||||||
|
"""
|
||||||
|
Wrapper that handles RefScalars, as with profile objects.
|
||||||
|
"""
|
||||||
|
# Turns out (somewhat intentionally) that every spline parameter used on
|
||||||
|
# the host has a matching parameter in the profile, so this
|
||||||
|
def __init__(self, val, other, spec=None, path=()):
|
||||||
|
super(RefWrapper, self).__init__(val, spec, path)
|
||||||
|
self.other = other
|
||||||
|
|
||||||
|
def wrap_dict(self, path, spec, val):
|
||||||
|
return type(self)(val or {}, self.other, spec, path)
|
||||||
|
|
||||||
|
def wrap_refscalar(self, path, spec, val):
|
||||||
|
spev = self.other
|
||||||
|
for part in spec.ref.split('.'):
|
||||||
|
spev = spev[part]
|
||||||
|
spev *= val if val is not None else spec.default
|
||||||
|
return spev
|
||||||
|
|
||||||
|
class SplineWrapper(Wrapper):
|
||||||
|
def wrap_spline(self, path, spec, val):
|
||||||
|
return SplineEval(val if val is not None else spec.default,
|
||||||
|
spec.interp)
|
||||||
|
|
||||||
|
class SplineEval(object):
|
||||||
|
_mat = np.matrix([[1.,-2, 1, 0], [2,-3, 0, 1],
|
||||||
|
[1,-1, 0, 0], [-2, 3, 0, 0]])
|
||||||
|
_deriv = np.matrix(np.diag([3,2,1], 1))
|
||||||
|
|
||||||
|
def __init__(self, knots, interp='linear'):
|
||||||
|
self.knots, self.interp = self.normalize(knots), interp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def normalize(knots):
|
||||||
|
if isinstance(knots, (int, float)):
|
||||||
|
v0, v1 = 0, 0
|
||||||
|
knots = [(0, knots), (1, knots)]
|
||||||
|
elif len(knots) % 2 != 0:
|
||||||
|
raise ValueError("List with odd number of elements given")
|
||||||
|
elif len(knots) == 2:
|
||||||
|
v0, v1 = 0, 0
|
||||||
|
knots = [(0, knots[0]), (1, knots[1])]
|
||||||
|
else:
|
||||||
|
p0, v0, p1, v1 = knots[:4]
|
||||||
|
knots = [(0, p0), (1, p1)] + zip(knots[4::2], knots[5::2])
|
||||||
|
|
||||||
|
knots = sorted(knots)
|
||||||
|
|
||||||
|
# If stabilizing knots are missing before or after the edges of the
|
||||||
|
# [0,1] interval, add them. In almost all cases, the precise timing of
|
||||||
|
# the end knots has little affect on the shape of the curve.
|
||||||
|
td = 2
|
||||||
|
if knots[0][0] >= 0:
|
||||||
|
knots = [(-td, knots[1][1] - (knots[1][0] - (-td)) * v0)] + knots
|
||||||
|
if knots[-1][0] <= 1:
|
||||||
|
knots.extend([(1+td, knots[-2][1] + (1+td - knots[-2][0]) * v1)])
|
||||||
|
|
||||||
|
knotarray = np.zeros((2, len(knots)))
|
||||||
|
knotarray.T[:] = knots
|
||||||
|
return knotarray
|
||||||
|
|
||||||
|
def find_knots(self, itime):
|
||||||
|
idx = np.searchsorted(self.knots[0], itime) - 2
|
||||||
|
idx = max(0, min(idx, len(self.knots[0]) - 4))
|
||||||
|
|
||||||
|
times = self.knots[0][idx:idx+4]
|
||||||
|
vals = self.knots[1][idx:idx+4]
|
||||||
|
# Normalize to [0,1]
|
||||||
|
t = itime - times[1]
|
||||||
|
times = times - times[1]
|
||||||
|
scale = 1 / times[2]
|
||||||
|
t = t * scale
|
||||||
|
times = times * scale
|
||||||
|
return times, vals, t, scale
|
||||||
|
|
||||||
|
def __call__(self, itime, deriv=0):
|
||||||
|
times, vals, t, scale = self.find_knots(itime)
|
||||||
|
|
||||||
|
m1 = (vals[2] - vals[0]) / (1.0 - times[0])
|
||||||
|
m2 = (vals[3] - vals[1]) / times[3]
|
||||||
|
|
||||||
|
mat = self._mat
|
||||||
|
if deriv:
|
||||||
|
mat = mat * (scale * self._deriv) ** deriv
|
||||||
|
val = [m1, vals[1], m2, vals[2]] * mat * np.array([[t**3, t**2, t, 1]]).T
|
||||||
|
return val[0,0]
|
||||||
|
|
||||||
|
def __imul__(self, other):
|
||||||
|
self.knots[1] *= other
|
||||||
|
return self
|
||||||
|
|
||||||
|
def _plt(self, name='SplEval', fig=111, show=True):
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
x = np.linspace(-0.0, 1.0, 500)
|
||||||
|
r = x[1] - x[0]
|
||||||
|
plt.figure(fig)
|
||||||
|
plt.title(name)
|
||||||
|
plt.plot(x,map(self,x),x,[self(i,1) for i in x],'--',
|
||||||
|
self.knots[0],self.knots[1],'x')
|
||||||
|
plt.xlim(0.0, 1.0)
|
||||||
|
if show:
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def wrap_genome(prof, gnm):
|
||||||
|
# It's not obvious that this is what needs to happen, so we wrap. The
|
||||||
|
# timing is simplistic, and may get expanded or moved later.
|
||||||
|
gprof = RefWrapper(prof, SplineWrapper(gnm), toplevels['profile'])
|
||||||
|
nframes = round(gprof.fps * gprof.duration)
|
||||||
|
times = np.linspace(0, 1, nframes + 1)
|
||||||
|
times = times[:-1] + 0.5 * (times[1] - times[0])
|
||||||
|
return gprof, times
|
122
cuburn/genome/util.py
Normal file
122
cuburn/genome/util.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import base64
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from cuburn.code.util import crep
|
||||||
|
|
||||||
|
def get(dct, default, *keys):
|
||||||
|
if len(keys) == 1:
|
||||||
|
keys = keys[0].split('.')
|
||||||
|
for k in keys:
|
||||||
|
if k in dct:
|
||||||
|
dct = dct[k]
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
return dct
|
||||||
|
|
||||||
|
def flatten(dct, ctx=()):
|
||||||
|
"""
|
||||||
|
Given a nested dict, return a flattened dict with dot-separated string
|
||||||
|
keys. Keys that have dots in them already are treated the same.
|
||||||
|
|
||||||
|
>>> flatten({'ab': {'xy.zw': 1}, 4: 5}) == {'ab.xy.zw': 1, '4': 5}
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
|
||||||
|
for k, v in dct.items():
|
||||||
|
k = str(k)
|
||||||
|
if isinstance(v, dict):
|
||||||
|
for sk, sv in flatten(v, ctx + (k,)):
|
||||||
|
yield sk, sv
|
||||||
|
else:
|
||||||
|
yield '.'.join(ctx + (k,)), v
|
||||||
|
|
||||||
|
def unflatten(kvlist):
|
||||||
|
"""
|
||||||
|
Given a flattened dict, return a nested dict, where every dot-separated
|
||||||
|
key is converted into a sub-dict.
|
||||||
|
|
||||||
|
>>> (unflatten([('ab.xy.zw', 1), ('4', 5)) ==
|
||||||
|
... {'ab': {'xy': {'zw': 1}}, '4': 5})
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
def go(d, k, v):
|
||||||
|
if len(k) == 1:
|
||||||
|
d[k[0]] = v
|
||||||
|
else:
|
||||||
|
go(d.setdefault(k[0], {}), k[1:], v)
|
||||||
|
out = {}
|
||||||
|
for k, v in kvlist:
|
||||||
|
go(out, k.split('.'), v)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def palette_decode(datastrs):
|
||||||
|
"""
|
||||||
|
Decode a palette (stored as a list suitable for JSON packing) into a
|
||||||
|
palette. Internal palette format is simply as a (256,4) array of [0,1]
|
||||||
|
RGBA floats.
|
||||||
|
"""
|
||||||
|
if datastrs[0] != 'rgb8':
|
||||||
|
raise NotImplementedError
|
||||||
|
raw = base64.b64decode(''.join(datastrs[1:]))
|
||||||
|
pal = np.reshape(np.fromstring(raw, np.uint8), (256, 3))
|
||||||
|
data = np.ones((256, 4), np.float32)
|
||||||
|
data[:,:3] = pal / 255.0
|
||||||
|
return data
|
||||||
|
|
||||||
|
def palette_encode(data, format='rgb8'):
|
||||||
|
"""
|
||||||
|
Encode an internal-format palette to an external representation.
|
||||||
|
"""
|
||||||
|
if format != 'rgb8':
|
||||||
|
raise NotImplementedError
|
||||||
|
clamp = np.maximum(0, np.minimum(255, np.round(data[:,:3]*255.0)))
|
||||||
|
enc = base64.b64encode(np.uint8(clamp))
|
||||||
|
return ['rgb8'] + [enc[i:i+64] for i in range(0, len(enc), 64)]
|
||||||
|
|
||||||
|
def json_encode(obj):
|
||||||
|
"""
|
||||||
|
Encode an object into JSON notation, formatted to be more readable than
|
||||||
|
the output of the standard 'json' package for genomes.
|
||||||
|
|
||||||
|
This serializer only works on the subset of JSON used in genomes.
|
||||||
|
"""
|
||||||
|
result = _js_enc_obj(obj).lstrip()
|
||||||
|
result = '\n'.join(l.rstrip() for l in result.split('\n'))
|
||||||
|
return result + '\n'
|
||||||
|
|
||||||
|
def _js_enc_obj(obj, indent=0):
|
||||||
|
isnum = lambda v: isinstance(v, (float, int, np.number))
|
||||||
|
|
||||||
|
def wrap(pairs, delims):
|
||||||
|
do, dc = delims
|
||||||
|
i = ' ' * indent
|
||||||
|
out = ''.join([do, ', '.join(pairs), dc])
|
||||||
|
if '\n' not in out and len(out) + indent < 70:
|
||||||
|
return out
|
||||||
|
return ''.join(['\n', i, do, ' ', ('\n'+i+', ').join(pairs),
|
||||||
|
'\n', i, dc])
|
||||||
|
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
if not obj:
|
||||||
|
return '{}'
|
||||||
|
digsort = lambda kv: (int(kv[0]), kv[1]) if kv[0].isdigit() else kv
|
||||||
|
ks, vs = zip(*sorted(obj.items(), key=digsort))
|
||||||
|
if ks == ('b', 'g', 'r'):
|
||||||
|
ks, vs = reversed(ks), reversed(vs)
|
||||||
|
ks = [crep('%.6g' % k if isnum(k) else str(k)) for k in ks]
|
||||||
|
vs = [_js_enc_obj(v, indent+2) for v in vs]
|
||||||
|
return wrap(['%s: %s' % p for p in zip(ks, vs)], '{}')
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
vs = [_js_enc_obj(v, indent+2) for v in obj]
|
||||||
|
if vs and len(vs) % 2 == 0 and isnum(obj[1]):
|
||||||
|
vs = map(', '.join, zip(vs[::2], vs[1::2]))
|
||||||
|
return wrap(vs, '[]')
|
||||||
|
#elif isinstance(obj, SplEval):
|
||||||
|
#return _js_enc_obj(obj.knotlist, indent)
|
||||||
|
elif isinstance(obj, basestring):
|
||||||
|
return crep(obj)
|
||||||
|
elif isnum(obj):
|
||||||
|
return '%.6g' % obj
|
||||||
|
raise TypeError("Don't know how to serialize %s of type %s" %
|
||||||
|
(obj, type(obj)))
|
127
cuburn/genome/variations.py
Normal file
127
cuburn/genome/variations.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
from spectypes import spline, scalespline
|
||||||
|
import numpy as np
|
||||||
|
# Pre-instantiated default splines. Used a *lot*.
|
||||||
|
s, ss, sz = spline(), scalespline(), scalespline(min=0)
|
||||||
|
|
||||||
|
__all__ = ["var_names", "var_params"]
|
||||||
|
|
||||||
|
# A map from flam3 variation numbers to variation names. Some variations may
|
||||||
|
# not be included in this list if they don't yet exist in flam3.
|
||||||
|
var_names = {}
|
||||||
|
|
||||||
|
# A map from variation names to a dict of parameter types, suitable for
|
||||||
|
# inclusion in the genome schema.
|
||||||
|
var_params = {}
|
||||||
|
|
||||||
|
def var(num, name, **params):
|
||||||
|
if num is not None:
|
||||||
|
var_names[num] = name
|
||||||
|
# Mark as a variation parameter spline. This can be handled in various
|
||||||
|
# ways by interpolation - usually by copying a value when missing, instead
|
||||||
|
# of reading the default value.
|
||||||
|
for k, v in params.items():
|
||||||
|
params[k] = v._replace(var=True)
|
||||||
|
params['weight'] = scalespline(0)
|
||||||
|
var_params[name] = params
|
||||||
|
|
||||||
|
# TODO: review all parameter splines, possibly programmatically
|
||||||
|
var(0, 'linear')
|
||||||
|
var(1, 'sinusoidal')
|
||||||
|
var(2, 'spherical')
|
||||||
|
var(3, 'swirl')
|
||||||
|
var(4, 'horseshoe')
|
||||||
|
var(5, 'polar')
|
||||||
|
var(6, 'handkerchief')
|
||||||
|
var(7, 'heart')
|
||||||
|
var(8, 'disc')
|
||||||
|
var(9, 'spiral')
|
||||||
|
var(10, 'hyperbolic')
|
||||||
|
var(11, 'diamond')
|
||||||
|
var(12, 'ex')
|
||||||
|
var(13, 'julia')
|
||||||
|
var(14, 'bent')
|
||||||
|
var(15, 'waves')
|
||||||
|
var(16, 'fisheye')
|
||||||
|
var(17, 'popcorn')
|
||||||
|
var(18, 'exponential')
|
||||||
|
var(19, 'power')
|
||||||
|
var(20, 'cosine')
|
||||||
|
var(21, 'rings')
|
||||||
|
var(22, 'fan')
|
||||||
|
var(23, 'blob', low=ss, high=ss, waves=ss)
|
||||||
|
var(24, 'pdj', a=s, b=s, c=s, d=s)
|
||||||
|
var(25, 'fan2', x=s, y=s)
|
||||||
|
var(26, 'rings2', val=s)
|
||||||
|
var(27, 'eyefish')
|
||||||
|
var(28, 'bubble')
|
||||||
|
var(29, 'cylinder')
|
||||||
|
var(30, 'perspective', angle=s, dist=s) # TODO: period
|
||||||
|
var(31, 'noise')
|
||||||
|
var(32, 'julian', power=ss, dist=ss)
|
||||||
|
var(33, 'juliascope', power=ss, dist=ss)
|
||||||
|
var(34, 'blur')
|
||||||
|
var(35, 'gaussian_blur')
|
||||||
|
var(36, 'radial_blur', angle=spline(period=4))
|
||||||
|
var(37, 'pie', slices=spline(6, 1), rotation=s, thickness=spline(0.5, 0, 1))
|
||||||
|
var(38, 'ngon', sides=spline(5), power=spline(3),
|
||||||
|
circle=spline(1), corners=spline(2))
|
||||||
|
var(39, 'curl', c1=spline(1), c2=s) # TODO: not identity?
|
||||||
|
|
||||||
|
var(40, 'rectangles', x=s, y=s)
|
||||||
|
var(41, 'arch')
|
||||||
|
var(42, 'tangent')
|
||||||
|
var(43, 'square')
|
||||||
|
var(44, 'rays')
|
||||||
|
var(45, 'blade')
|
||||||
|
var(46, 'secant2')
|
||||||
|
var(48, 'cross')
|
||||||
|
var(49, 'disc2', rot=s, twist=s)
|
||||||
|
var(50, 'super_shape', rnd=s, m=s, n1=ss, n2=spline(1), n3=spline(1), holes=s)
|
||||||
|
var(51, 'flower', holes=s, petals=s)
|
||||||
|
var(52, 'conic', holes=s, eccentricity=spline(1))
|
||||||
|
var(53, 'parabola', height=ss, width=ss)
|
||||||
|
var(54, 'bent2', x=ss, y=ss)
|
||||||
|
var(55, 'bipolar', shift=s)
|
||||||
|
var(56, 'boarders')
|
||||||
|
var(57, 'butterfly')
|
||||||
|
var(58, 'cell', size=ss)
|
||||||
|
var(59, 'cpow', r=ss, i=s, power=ss)
|
||||||
|
var(60, 'curve', xamp=s, yamp=s, xlength=ss, ylength=ss)
|
||||||
|
var(61, 'edisc')
|
||||||
|
var(62, 'elliptic')
|
||||||
|
var(63, 'escher', beta=spline(period=2*np.pi))
|
||||||
|
var(64, 'foci')
|
||||||
|
var(65, 'lazysusan', x=s, y=s, twist=s, space=s, spin=s)
|
||||||
|
var(66, 'loonie')
|
||||||
|
var(67, 'pre_blur')
|
||||||
|
var(68, 'modulus', x=s, y=s)
|
||||||
|
var(69, 'oscope', separation=spline(1), frequency=scalespline(np.pi),
|
||||||
|
amplitude=ss, damping=s)
|
||||||
|
var(70, 'polar2')
|
||||||
|
var(71, 'popcorn2', x=s, y=s, c=s)
|
||||||
|
var(72, 'scry')
|
||||||
|
var(73, 'separation', x=s, xinside=s, y=s, yinside=s)
|
||||||
|
var(74, 'split', xsize=s, ysize=s)
|
||||||
|
var(75, 'splits', x=s, y=s)
|
||||||
|
var(76, 'stripes', space=s, warp=s)
|
||||||
|
var(77, 'wedge', angle=s, hole=s, count=ss, swirl=s)
|
||||||
|
var(80, 'whorl', inside=s, outside=s)
|
||||||
|
var(81, 'waves2', scalex=ss, scaley=ss,
|
||||||
|
freqx=scalespline(np.pi), freqy=scalespline(np.pi))
|
||||||
|
var(82, 'exp')
|
||||||
|
var(83, 'log')
|
||||||
|
var(84, 'sin')
|
||||||
|
var(85, 'cos')
|
||||||
|
var(86, 'tan')
|
||||||
|
var(87, 'sec')
|
||||||
|
var(88, 'csc')
|
||||||
|
var(89, 'cot')
|
||||||
|
var(90, 'sinh')
|
||||||
|
var(91, 'cosh')
|
||||||
|
var(92, 'tanh')
|
||||||
|
var(93, 'sech')
|
||||||
|
var(94, 'csch')
|
||||||
|
var(95, 'coth')
|
||||||
|
var(97, 'flux', spread=s)
|
||||||
|
var(98, 'mobius', re_a=s, im_a=s, re_b=s, im_b=s,
|
||||||
|
re_c=s, im_c=s, re_d=s, im_d=s)
|
@ -18,6 +18,7 @@ import filters
|
|||||||
import output
|
import output
|
||||||
from code import util, mwc, iter, interp, sort
|
from code import util, mwc, iter, interp, sort
|
||||||
from code.util import ClsMod, devlib, filldptrlib, assemble_code, launch
|
from code.util import ClsMod, devlib, filldptrlib, assemble_code, launch
|
||||||
|
from cuburn.genome.util import palette_decode
|
||||||
|
|
||||||
RenderedImage = namedtuple('RenderedImage', 'buf idx gpu_time')
|
RenderedImage = namedtuple('RenderedImage', 'buf idx gpu_time')
|
||||||
Dimensions = namedtuple('Dimensions', 'w h aw ah astride')
|
Dimensions = namedtuple('Dimensions', 'w h aw ah astride')
|
||||||
@ -200,7 +201,7 @@ class Renderer(object):
|
|||||||
MAX_MODREFS = 20
|
MAX_MODREFS = 20
|
||||||
_modrefs = []
|
_modrefs = []
|
||||||
|
|
||||||
def __init__(self, gnm):
|
def __init__(self, gnm, gprof):
|
||||||
self.packer, self.lib = iter.mkiterlib(gnm)
|
self.packer, self.lib = iter.mkiterlib(gnm)
|
||||||
cubin = util.compile('iter', assemble_code(self.lib))
|
cubin = util.compile('iter', assemble_code(self.lib))
|
||||||
self.mod = cuda.module_from_buffer(cubin)
|
self.mod = cuda.module_from_buffer(cubin)
|
||||||
@ -210,9 +211,7 @@ class Renderer(object):
|
|||||||
self._modrefs.append(self.mod)
|
self._modrefs.append(self.mod)
|
||||||
|
|
||||||
# TODO: make these customizable
|
# TODO: make these customizable
|
||||||
self.filts = [ filters.Bilateral()
|
self.filts = filters.create(gprof)
|
||||||
, filters.Logscale()
|
|
||||||
, filters.ColorClip() ]
|
|
||||||
self.out = output.PILOutput()
|
self.out = output.PILOutput()
|
||||||
|
|
||||||
class RenderManager(ClsMod):
|
class RenderManager(ClsMod):
|
||||||
@ -236,13 +235,14 @@ class RenderManager(ClsMod):
|
|||||||
Note that for now, this is broken! It ignores ``gnm``, and only packs
|
Note that for now, this is broken! It ignores ``gnm``, and only packs
|
||||||
the genome that was used when creating the renderer.
|
the genome that was used when creating the renderer.
|
||||||
"""
|
"""
|
||||||
times, knots = rdr.packer.pack(self.pool)
|
times, knots = rdr.packer.pack(gnm, self.pool)
|
||||||
cuda.memcpy_htod_async(self.src_a.d_times, times, self.stream_a)
|
cuda.memcpy_htod_async(self.src_a.d_times, times, self.stream_a)
|
||||||
cuda.memcpy_htod_async(self.src_a.d_knots, knots, self.stream_a)
|
cuda.memcpy_htod_async(self.src_a.d_knots, knots, self.stream_a)
|
||||||
|
|
||||||
ptimes, pidxs = zip(*gnm.palette_times)
|
palsrc = dict([(v[0], palette_decode(v[1:])) for v in gnm['palette']])
|
||||||
palettes = self.pool.allocate((len(ptimes), 256, 4), f32)
|
ptimes, pvals = zip(*sorted(palsrc.items()))
|
||||||
palettes[:] = [gnm.decoded_palettes[i] for i in pidxs]
|
palettes = self.pool.allocate((len(palsrc), 256, 4), f32)
|
||||||
|
palettes[:] = pvals
|
||||||
palette_times = self.pool.allocate((self.src_a.max_knots,), f32)
|
palette_times = self.pool.allocate((self.src_a.max_knots,), f32)
|
||||||
palette_times.fill(1e9)
|
palette_times.fill(1e9)
|
||||||
palette_times[:len(ptimes)] = ptimes
|
palette_times[:len(ptimes)] = ptimes
|
||||||
@ -271,7 +271,7 @@ class RenderManager(ClsMod):
|
|||||||
256, np.ceil(nts / 256.),
|
256, np.ceil(nts / 256.),
|
||||||
self.info_a.d_params, self.src_a.d_times, self.src_a.d_knots,
|
self.info_a.d_params, self.src_a.d_times, self.src_a.d_knots,
|
||||||
f32(ts), f32(td / nts), i32(nts))
|
f32(ts), f32(td / nts), i32(nts))
|
||||||
#self._print_interp_knots(rdr)
|
self._print_interp_knots(rdr)
|
||||||
|
|
||||||
def _print_interp_knots(self, rdr, tsidx=5):
|
def _print_interp_knots(self, rdr, tsidx=5):
|
||||||
infos = cuda.from_device(self.info_a.d_params,
|
infos = cuda.from_device(self.info_a.d_params,
|
||||||
@ -279,7 +279,7 @@ class RenderManager(ClsMod):
|
|||||||
for i, n in zip(infos[-1], rdr.packer.packed):
|
for i, n in zip(infos[-1], rdr.packer.packed):
|
||||||
print '%60s %g' % ('_'.join(n), i)
|
print '%60s %g' % ('_'.join(n), i)
|
||||||
|
|
||||||
def _iter(self, rdr, gnm, dim, tc):
|
def _iter(self, rdr, gnm, gprof, dim, tc):
|
||||||
tref = rdr.mod.get_surfref('flatpal')
|
tref = rdr.mod.get_surfref('flatpal')
|
||||||
tref.set_array(self.info_a.d_pal_array, 0)
|
tref.set_array(self.info_a.d_pal_array, 0)
|
||||||
|
|
||||||
@ -291,19 +291,29 @@ class RenderManager(ClsMod):
|
|||||||
fill(self.fb.d_points, self.fb._len_d_points / 4, f32(np.nan))
|
fill(self.fb.d_points, self.fb._len_d_points / 4, f32(np.nan))
|
||||||
|
|
||||||
nts = self.info_a.ntemporal_samples
|
nts = self.info_a.ntemporal_samples
|
||||||
nsamps = (gnm.spp(tc) * dim.w * dim.h)
|
nsamps = (gprof.spp(tc) * dim.w * dim.h)
|
||||||
nrounds = int(nsamps / (nts * 256. * 256)) + 1
|
nrounds = int(nsamps / (nts * 256. * 256)) + 1
|
||||||
launch('iter', rdr.mod, self.stream_a, (32, 8, 1), (nts, nrounds),
|
|
||||||
|
def launch_iter(n):
|
||||||
|
if n == 0: return
|
||||||
|
launch('iter', rdr.mod, self.stream_a, (32, 8, 1), (nts, n),
|
||||||
self.fb.d_front, self.fb.d_side,
|
self.fb.d_front, self.fb.d_side,
|
||||||
self.fb.d_rb, self.fb.d_seeds, self.fb.d_points,
|
self.fb.d_rb, self.fb.d_seeds, self.fb.d_points,
|
||||||
self.info_a.d_params)
|
self.info_a.d_params)
|
||||||
|
# Split the launch into multiple rounds, possibly (slightly) reducing
|
||||||
|
# work overlap but avoiding stalls when working on a device with an
|
||||||
|
# active X session. TODO: characterize performance impact, autodetect
|
||||||
|
BLOCK_SIZE = 4
|
||||||
|
for i in range(BLOCK_SIZE-1, nrounds, BLOCK_SIZE):
|
||||||
|
launch_iter(BLOCK_SIZE)
|
||||||
|
launch_iter(nrounds%BLOCK_SIZE)
|
||||||
|
|
||||||
nblocks = int(np.ceil(np.sqrt(dim.ah*dim.astride/256.)))
|
nblocks = int(np.ceil(np.sqrt(dim.ah*dim.astride/256.)))
|
||||||
launch('flush_atom', self.mod, self.stream_a,
|
launch('flush_atom', self.mod, self.stream_a,
|
||||||
256, (nblocks, nblocks),
|
256, (nblocks, nblocks),
|
||||||
u64(self.fb.d_front), u64(self.fb.d_side), i32(nbins))
|
u64(self.fb.d_front), u64(self.fb.d_side), i32(nbins))
|
||||||
|
|
||||||
def queue_frame(self, rdr, gnm, tc, w, h, copy=True):
|
def queue_frame(self, rdr, gnm, gprof, tc, copy=True):
|
||||||
"""
|
"""
|
||||||
Queue one frame for rendering.
|
Queue one frame for rendering.
|
||||||
|
|
||||||
@ -332,9 +342,10 @@ class RenderManager(ClsMod):
|
|||||||
"""
|
"""
|
||||||
# Note: we synchronize on the previous stream if buffers need to be
|
# Note: we synchronize on the previous stream if buffers need to be
|
||||||
# reallocated, which implicitly also syncs the current stream.
|
# reallocated, which implicitly also syncs the current stream.
|
||||||
dim = self.fb.set_dim(w, h, self.stream_b)
|
dim = self.fb.set_dim(gprof.width, gprof.height, self.stream_b)
|
||||||
|
|
||||||
td = gnm.adj_frame_width(tc)
|
# TODO: calculate this externally somewhere?
|
||||||
|
td = gprof.frame_width(tc) / round(gprof.fps * gprof.duration)
|
||||||
ts, te = tc - 0.5 * td, tc + 0.5 * td
|
ts, te = tc - 0.5 * td, tc + 0.5 * td
|
||||||
|
|
||||||
# The stream interleaving here is nontrivial.
|
# The stream interleaving here is nontrivial.
|
||||||
@ -345,12 +356,12 @@ class RenderManager(ClsMod):
|
|||||||
self._interp(rdr, gnm, dim, ts, td)
|
self._interp(rdr, gnm, dim, ts, td)
|
||||||
if self.filt_evt:
|
if self.filt_evt:
|
||||||
self.stream_a.wait_for_event(self.filt_evt)
|
self.stream_a.wait_for_event(self.filt_evt)
|
||||||
self._iter(rdr, gnm, dim, tc)
|
self._iter(rdr, gnm, gprof, dim, tc)
|
||||||
if self.copy_evt:
|
if self.copy_evt:
|
||||||
self.stream_a.wait_for_event(self.copy_evt)
|
self.stream_a.wait_for_event(self.copy_evt)
|
||||||
for filt in rdr.filts:
|
for filt, params in zip(rdr.filts, gprof.filters):
|
||||||
filt.apply(self.fb, gnm, dim, tc, self.stream_a)
|
filt.apply(self.fb, gprof, params, dim, tc, self.stream_a)
|
||||||
rdr.out.convert(self.fb, gnm, dim, self.stream_a)
|
rdr.out.convert(self.fb, gprof, dim, self.stream_a)
|
||||||
self.filt_evt = cuda.Event().record(self.stream_a)
|
self.filt_evt = cuda.Event().record(self.stream_a)
|
||||||
h_out = rdr.out.copy(self.fb, dim, self.pool, self.stream_a)
|
h_out = rdr.out.copy(self.fb, dim, self.pool, self.stream_a)
|
||||||
self.copy_evt = cuda.Event().record(self.stream_a)
|
self.copy_evt = cuda.Event().record(self.stream_a)
|
||||||
@ -359,13 +370,13 @@ class RenderManager(ClsMod):
|
|||||||
self.stream_a, self.stream_b = self.stream_b, self.stream_a
|
self.stream_a, self.stream_b = self.stream_b, self.stream_a
|
||||||
return self.copy_evt, h_out
|
return self.copy_evt, h_out
|
||||||
|
|
||||||
def render(self, gnm, times, w, h):
|
def render(self, gnm, gprof, times):
|
||||||
"""
|
"""
|
||||||
A port of the old rendering function, retained for backwards
|
A port of the old rendering function, retained for backwards
|
||||||
compatibility. Some of this will be pulled into as-yet-undecided
|
compatibility. Some of this will be pulled into as-yet-undecided
|
||||||
methods for more DRY.
|
methods for more DRY.
|
||||||
"""
|
"""
|
||||||
rdr = Renderer(gnm)
|
rdr = Renderer(gnm, gprof)
|
||||||
last_evt = cuda.Event().record(self.stream_a)
|
last_evt = cuda.Event().record(self.stream_a)
|
||||||
last_idx = None
|
last_idx = None
|
||||||
def wait(): # Times like these where you wish for a macro
|
def wait(): # Times like these where you wish for a macro
|
||||||
@ -374,7 +385,7 @@ class RenderManager(ClsMod):
|
|||||||
gpu_time = last_evt.time_since(two_evts_ago)
|
gpu_time = last_evt.time_since(two_evts_ago)
|
||||||
return RenderedImage(last_buf, last_idx, gpu_time)
|
return RenderedImage(last_buf, last_idx, gpu_time)
|
||||||
for idx, tc in times:
|
for idx, tc in times:
|
||||||
evt, h_buf = self.queue_frame(rdr, gnm, tc, w, h, last_idx is None)
|
evt, h_buf = self.queue_frame(rdr, gnm, gprof, tc, last_idx is None)
|
||||||
if last_idx:
|
if last_idx:
|
||||||
yield wait()
|
yield wait()
|
||||||
two_evts_ago, last_evt = last_evt, evt
|
two_evts_ago, last_evt = last_evt, evt
|
||||||
|
20
main.py
20
main.py
@ -22,13 +22,14 @@ from itertools import ifilter
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pycuda.driver as cuda
|
import pycuda.driver as cuda
|
||||||
|
|
||||||
from cuburn import genome, render, filters, output
|
from cuburn import render, filters, output
|
||||||
|
from cuburn.genome import convert, use
|
||||||
|
|
||||||
profiles = {
|
profiles = {
|
||||||
'1080p': dict(fps=24, width=1920, height=1080, quality=3000, skip=0),
|
'1080p': dict(width=1920, height=1080),
|
||||||
'720p': dict(fps=24, width=1280, height=720, quality=2500, skip=0),
|
'720p': dict(width=1280, height=720),
|
||||||
'540p': dict(fps=24, width=960, height=540, quality=2500, skip=0),
|
'540p': dict(width=960, height=540),
|
||||||
'preview': dict(fps=24, width=640, height=360, quality=800, skip=1)
|
'preview': dict(width=640, height=360, quality=800, skip=1)
|
||||||
}
|
}
|
||||||
|
|
||||||
def save(out):
|
def save(out):
|
||||||
@ -41,15 +42,14 @@ def main(args, prof):
|
|||||||
|
|
||||||
gnm_str = args.flame.read()
|
gnm_str = args.flame.read()
|
||||||
if '<' in gnm_str[:10]:
|
if '<' in gnm_str[:10]:
|
||||||
flames = genome.XMLGenomeParser.parse(gnm_str)
|
flames = convert.XMLGenomeParser.parse(gnm_str)
|
||||||
if len(flames) != 1:
|
if len(flames) != 1:
|
||||||
warnings.warn('%d flames in file, only using one.' % len(flames))
|
warnings.warn('%d flames in file, only using one.' % len(flames))
|
||||||
gnm = genome.convert_flame(flames[0])
|
gnm = convert.convert_flame(flames[0])
|
||||||
else:
|
else:
|
||||||
gnm = json.loads(gnm_str)
|
gnm = json.loads(gnm_str)
|
||||||
gnm = genome.Genome(gnm)
|
|
||||||
err, times = gnm.set_profile(prof)
|
|
||||||
|
|
||||||
|
gprof, times = use.wrap_genome(prof, gnm)
|
||||||
rmgr = render.RenderManager()
|
rmgr = render.RenderManager()
|
||||||
|
|
||||||
basename = os.path.basename(args.flame.name).rsplit('.', 1)[0] + '_'
|
basename = os.path.basename(args.flame.name).rsplit('.', 1)[0] + '_'
|
||||||
@ -71,7 +71,7 @@ def main(args, prof):
|
|||||||
if not os.path.isfile(f[0]) or m > os.path.getmtime(f[0]))
|
if not os.path.isfile(f[0]) or m > os.path.getmtime(f[0]))
|
||||||
|
|
||||||
w, h = prof['width'], prof['height']
|
w, h = prof['width'], prof['height']
|
||||||
gen = rmgr.render(gnm, frames, w, h)
|
gen = rmgr.render(gnm, gprof, frames)
|
||||||
|
|
||||||
if not args.gfx:
|
if not args.gfx:
|
||||||
for out in gen:
|
for out in gen:
|
||||||
|
Loading…
Reference in New Issue
Block a user