Commit c1ad61dc authored by Adam P. Goucher's avatar Adam P. Goucher

Functionality to display patterns using Jupyter + LifeViewer

parent 9e30c918
......@@ -63,6 +63,20 @@ extern "C"
delete reinterpret_cast<apg::pattern*>(ptr);
}
void SavePatternRLE(void *ptr, const char *filename, const char *header, const char *footer) {
std::cerr << "filename: '" << filename << "'\n";
std::cerr << "header: '" << header << "'\n";
std::cerr << "footer: '" << footer << "'\n";
std::cerr << "--------" << std::endl;
std::ofstream out(filename);
auto ppat = reinterpret_cast<apg::pattern*>(ptr);
out << header;
ppat->write_rle(out);
out << footer << std::endl;
}
void* CreatePatternFromFile(void* lt, const char *filename) {
auto lab = reinterpret_cast<apg::lifetree_abstract<uint32_t>*>(lt);
return new(std::nothrow) apg::pattern(lab, std::string(filename));
......
......@@ -112,6 +112,131 @@ namespace apg {
}
}
void getcells_recurse(hypernode<I> hnode, uint64_t x, uint64_t y, std::map<std::pair<uint64_t, uint64_t>, uint64_t> &cells) {
if (hnode.index == 0) {
return;
} else if (hnode.depth != 0) {
uint64_t semisize = 8ull << hnode.depth;
getcells_recurse(getchild(hnode, 0), x, y, cells);
getcells_recurse(getchild(hnode, 1), x + semisize, y, cells);
getcells_recurse(getchild(hnode, 2), x, y + semisize, cells);
getcells_recurse(getchild(hnode, 3), x + semisize, y + semisize, cells);
} else {
kiventry<nicearray<uint64_t, 4*N>, I, J >* pptr = ind2ptr_leaf(hnode.index);
uint64_t bitmask[4] = {0ull};
for (uint64_t i = 0; i < 4*N; i++) {
bitmask[i & 3] |= pptr->key.x[i];
}
for (uint64_t j = 0; j < 4; j++) {
if (bitmask[j] == 0) { continue; }
for (uint64_t yi = 0; yi < 8; yi++) {
for (uint64_t xi = 0; xi < 8; xi++) {
if (bitmask[j] & (1ull << (xi + (yi << 3)))) {
uint64_t c = 0;
for (unsigned int i = 0; i < N; i++) {
uint64_t rel = pptr->key.x[4*i + j];
rel = rel >> xi;
rel = rel >> (yi << 3);
c |= ((rel & 1) << i);
}
std::pair<uint64_t, uint64_t> loc(y + yi + ((j & 2) << 2), x + xi + ((j & 1) << 3));
cells.emplace(loc, c);
}
}
}
}
}
}
void printrun(std::ostream &outstream, uint64_t n, std::string c) {
if (n >= 2) {
outstream << n << c;
} else if (n == 1) {
outstream << c;
}
}
std::string state2string(uint64_t c) {
if (c == 0) {
return "b";
} else if (c == 1) {
return "o";
} else {
uint64_t d = c - 1;
std::string a(1, (char) ('A' + (d % 24)));
d = d / 24;
while (d > 0) {
a = std::string(1, (char) ('p' + ((d + 10) % 11))) + a;
d = d / 11;
}
return a;
}
}
void write_rle(std::ostream &outstream, hypernode<I> hnode, std::string rule) {
if (hnode.index == 0) {
outstream << "x = 1, y = 1, rule = " << rule << std::endl;
outstream << "b!" << std::endl;
} else {
std::map<std::pair<uint64_t, uint64_t>, uint64_t> cells;
getcells_recurse(hnode, 0, 0, cells);
uint64_t minx = -1;
uint64_t miny = -1;
uint64_t maxx = 0;
uint64_t maxy = 0;
for (auto it = cells.begin(); it != cells.end(); ++it) {
uint64_t y = it->first.first;
uint64_t x = it->first.second;
maxx = (x > maxx) ? x : maxx;
maxy = (y > maxy) ? y : maxy;
minx = (x < minx) ? x : minx;
miny = (y < miny) ? y : miny;
}
outstream << "x = " << (1 + maxx - minx) << ", y = " << (1 + maxy - miny) << ", rule = " << rule << std::endl;
uint64_t lastx = 0; uint64_t lasty = 0; uint64_t lastc = 0;
uint64_t runlength = 0;
for (auto it = cells.begin(); it != cells.end(); ++it) {
uint64_t y = it->first.first - miny;
uint64_t x = it->first.second - minx;
uint64_t c = it->second;
if ((y > lasty) || (x > lastx) || (c != lastc)) {
printrun(outstream, runlength, state2string(lastc));
if (y > lasty) {
printrun(outstream, y - lasty, "$");
lasty = y;
lastx = 0;
}
if (x > lastx) {
printrun(outstream, x - lastx, "b");
}
runlength = 0;
}
runlength += 1;
lastx = x + 1;
lastc = c;
}
printrun(outstream, runlength, state2string(lastc));
outstream << "!" << std::endl;
}
}
uint64_t write_macrocell_recurse(std::ostream &outstream, hypernode<I> hnode,
std::map<uint64_t, uint64_t> *subleaf2int,
std::map<std::pair<I, uint32_t>, uint64_t> *hnode2int,
......
......@@ -69,6 +69,7 @@ namespace apg {
virtual bool threshold_gc(uint64_t threshold) = 0;
virtual uint64_t getcell_recurse(hypernode<I> hnode, uint64_t x, uint64_t y) = 0;
virtual void write_macrocell(std::ostream &outstream, hypernode<I> hnode, std::string rule) = 0;
virtual void write_rle(std::ostream &outstream, hypernode<I> hnode, std::string rule) = 0;
virtual hypernode<I> _string32(std::string s) = 0;
virtual std::string _string32(hypernode<I> hnode) = 0;
......
......@@ -620,6 +620,10 @@ namespace apg {
lab->write_macrocell(outstream, hnode, rulestring);
}
void write_rle(std::ostream &outstream) {
lab->write_rle(outstream, hnode, rulestring);
}
};
template<typename I>
......
......@@ -4,10 +4,11 @@ from crt import chinese_remainder, large_primes
class Pattern(object):
def __init__(self, lifelib, ptr):
def __init__(self, session, ptr):
self.ptr = ptr
self.lifelib = lifelib
self.lifelib = session.lifelib
self.session = session
def __del__(self):
......@@ -27,15 +28,34 @@ class Pattern(object):
x = int(numgens if (abs(numgens) < max_num) else (numgens % max_num))
print("Advancing by %d, %d" % (x, exponent))
self.lifelib.AdvancePattern.restype = c_void_p
newptr = c_void_p(self.lifelib.AdvancePattern(temp.ptr, x, exponent))
temp = Pattern(self.lifelib, newptr)
temp = Pattern(self.session, newptr)
numgens = numgens - x
return temp
def write_rle(self, filename, header, footer):
self.lifelib.SavePatternRLE(self.ptr, filename, header, footer)
def viewer(self, filename=None, width=480, height=480,
lv_config='#C [[ THEME 6 GRID GRIDMAJOR 0 ]]'):
if filename is None:
filename = self.session.newfloat('viewer') + '.html'
header = '<div class="rle"><div style="display:none;"><code id="code2">\n'
footer = '#C [[ WIDTH %d HEIGHT %d ]]\n' % (width, height)
footer += '</code></div>\n<canvas width="%d" height="%d" style="margin-left:1px;"></canvas></div>\n' % (width+16, height+16)
footer += "<script type='text/javascript' src='http://www.conwaylife.com/js/lv-plugin.js'></script>\n"
self.write_rle(filename, header, lv_config + '\n' + footer)
from IPython.display import IFrame
return IFrame(filename, width=width+32, height=height+32)
def __getitem__(self, x):
if isinstance(x, Integral):
......@@ -52,8 +72,10 @@ class Pattern(object):
class Lifetree(object):
def __init__(self, lifelib, memory=1000, n_layers=1):
def __init__(self, session, memory=1000, n_layers=1):
self.session = session
lifelib = session.lifelib
self.lifelib = lifelib
lifelib.CreateLifetree.restype = c_void_p
self.ptr = c_void_p(lifelib.CreateLifetree(memory, n_layers))
......@@ -64,7 +86,7 @@ class Lifetree(object):
self.lifelib.CreatePatternFromRLE.restype = c_void_p
pptr = c_void_p(self.lifelib.CreatePatternFromRLE(self.ptr, rle, rule))
return Pattern(self.lifelib, pptr)
return Pattern(self.session, pptr)
def __enter__(self):
return self
......@@ -75,13 +97,22 @@ class Lifetree(object):
class Session(object):
def newfloat(self, name):
if name not in self.floats:
self.floats[name] = 0
self.floats[name] += 1
return ('%s%d' % (name, self.floats[name]))
def __init__(self, soname):
self.floats = {}
self.lifelib = cdll.LoadLibrary(soname)
def lifetree(self, *args, **kwargs):
return Lifetree(self.lifelib, *args, **kwargs)
return Lifetree(self, *args, **kwargs)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment