From 7d04f92b5551cae7a5c6d203910bca68a02d0a70 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Mon, 11 Jun 2018 02:24:10 +0200 Subject: [PATCH] added the Banach-Tarski animation to RogueViz --- hyper.h | 2 +- rogueviz-banachtarski.cpp | 522 ++++++++++++++++++++++++++++++++++++++ rogueviz-video.cpp | 89 ++++++- rogueviz.cpp | 2 + 4 files changed, 609 insertions(+), 6 deletions(-) create mode 100644 rogueviz-banachtarski.cpp diff --git a/hyper.h b/hyper.h index ea977ae0..3aed8947 100644 --- a/hyper.h +++ b/hyper.h @@ -1958,7 +1958,7 @@ template int addHook(hookset*& m, int prio, const U& hook) return 0; } -extern purehookset hooks_frame, hooks_stats, clearmemory, hooks_config, hooks_tests, hooks_removecells; +extern purehookset hooks_frame, hooks_stats, clearmemory, hooks_config, hooks_tests, hooks_removecells, hooks_initgame; template void callhooks(hookset *h, U... args) { if(h) for(auto& p: *h) p.second(args...); diff --git a/rogueviz-banachtarski.cpp b/rogueviz-banachtarski.cpp new file mode 100644 index 00000000..afc6e525 --- /dev/null +++ b/rogueviz-banachtarski.cpp @@ -0,0 +1,522 @@ +// Banach-Tarski animation in RogueViz. +// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details + +// good parameters: -fixx 10 -W Cros -bantar_anim +// use -bantar_map to see how it works + +namespace rogueviz::banachtarski { + +bool on; + +typedef vector cwpath; + +cwpath invertpath(cwpath p) { + cwpath res; + for(int i=0; i testlist; + +map parent; + +bool test_uniq(cellwalker cw, int z, int lev, cell *par) { + if(testlist.count(cw.c)) return false; + testlist.insert(cw.c); + if(par) parent[cw.c] = par; + if(celldist(cw.c) > 9) return true; + + /* if(cw.c->wall == waSea) { + printf("test_uniq failed\n"); + cw.c->wall = waEternalFire; + } + cw.c->wall = waSea; */ + if(lev) for(int y=0; y<4; y++) if(y != z) + if(!test_uniq(trace(cw, gens[y]), y^2, lev-1, cw.c)) + return false; + + return true; + } + +template void recursively(cell *c, cell *c1, T t) { + t(c); + for(int i=0; itype; i++) + if(c->mov[i] && c->spin(i) == 0 && c->mov[i] != c1) + recursively(c->mov[i], c, t); + } + +bool operator != (cellwalker c1, cellwalker c2) { + return c1.c != c2.c || c1.spin != c2.spin; + } + +vector allcells; + +set seen; + +struct cellinfo { + cell *c; + int gid; + int spdist; + eWall w; + eItem it; + eLand land; + eMonster mo; + vector way; + cwpath pinv; + }; + +map infos; + +int cidd = 0; + +int more = 5; + +void debugpath(vector& way, cwpath& pinv) { + printf("pinv:"); for(int i: pinv) printf(" %d", i); printf("\n"); + + cellwalker cw = cwt; + + cellwalker last_cw = cw; + int lastd = 0; + + printf("way:"); for(int i: way) { + cellwalker cw2 = trace(cw, pinv); + printf(" [%d]", lastd = celldist(cw2.c)); + printf(" %d", i); + last_cw = cw; + cw = trace(cw, gens[i]); + } + + cellwalker cw2 = trace(cw, pinv); + int curd; + printf(" [%d]", curd = celldist(cw2.c)); + printf("\n"); + + if(lastd == 10 && curd == 2) { + int b = way.back(); + way.pop_back(); + printf("CW:"); for(int w: way) for(int wp: gens[w]) printf(" %d", wp); + printf(" {"); + for(int wp: gens[b]) printf(" %d", wp); + printf(" }"); + for(int i: pinv) printf(" %d", i); + way.push_back(b); + printf("\n"); + } + } + + +void recursive_paint(cwpath& pinv, vector& way, int noway) { + cellwalker cw = cwt; + for(int i: way) cw = trace(cw, gens[i]); + cw = trace(cw, pinv); + cell *c = cw.c; + + /* if(cidd == 1 && way == vector{1,2,3}) + c->item = itPirate; */ + + if(seen.count(c)) { + printf("seen error [%d]\n", celldist(c)); + debugpath(way, pinv); + debugpath(infos[c].way, infos[c].pinv); + return; + } + seen.insert(c); + + int hsh = 7; + for(int w: way) hsh = 11301 * hsh + w * 37121; + + bool all0 = true; + for(int w: way) if(w) all0 = false; + + int gid; + + /* if(d) + c->landparam = 0x202020; + else */ + if(all0 || way[0] == 2) + gid = 0; + else if(way[0] == 0) + gid = 1; + else if(way[0] == 1) + gid = 2; + else if(way[0] == 3) + gid = 3; + else + gid = 3; + + infos[c] = cellinfo{c, gid, 0}; + infos[c].way = way; + infos[c].pinv = pinv; + + // c->landparam ^= ((size(way)&1) * 0x3F3F3F); + // c->landparam = hsh; // d * 5 + 256 * (hsh&0xFFFF) + 0x400000; + if(cidd>112899) c->landparam = 0x101010; + // c->landparam = cidd * 0x1241C3; + + if(celldist(c) <= 11+more) for(int i=0; i<4; i++) if(i != noway) { + vector newway = {i}; + for(int ii: way) newway.push_back(ii); + recursive_paint(pinv, newway, i^2); + } + } + +bool once = true; + +cwpath path_to(cell *c, int dir = 0) { + cwpath p; + cellwalker cw(c, dir); + while(cw != cwt) { + if(celldist((cw+wstep).c) < celldist(cw.c)) + p.push_back(0), cw += wstep; + else { + if(p.size() && p.back()) p.back()++; + else p.push_back(1); + cw += 1; + } + } + return p; + } + +void bantar_note(cell *c) { + if(seen.count(c)) return; + + cwpath pinv = invertpath(path_to(c)); + + vector way; + recursive_paint(pinv, way, 4); + cidd++; + } + +using bantar_config = pair; + +tuple quality(bantar_config cp) { + hyperpoint h1 = tC0(shmup::ggmatrix(cp.first)); + hyperpoint h2 = tC0(shmup::ggmatrix(cp.second)); + return make_tuple(hdist0(h1) * hdist0(h2), h2[1] > 0, abs(h2[0] / h2[1])); + } + +int notry = 0; + +void bantar() { + if(!on) return; + cwt = cellwalker(currentmap->gamestart(), 0); + viewctr = heptspin(cwt.c->master, 0); + infos.clear(); + + vector genchoices; + + int lnotry = notry; + + { + celllister clgen(cwt.c, 4, 1000000, NULL); + for(cell *c1: clgen.lst) if(c1->type == S7) + for(cell *c2: clgen.lst) if(c2->type == S7) + genchoices.emplace_back(c1, c2); + stable_sort(genchoices.begin(), genchoices.end(), [] (const bantar_config b1, const bantar_config b2) { return quality(b1) < quality(b2); }); + + for(bantar_config bc: genchoices) { + if(get<0>(quality(bc)) >= 4) exit(1); + for(int i=0; i(q), get<2>(q), celldist(bc.first), celldist(bc.second), tres); + lnotry--; if(lnotry <= 0) goto picked; + } + // if(tres) goto picked; + } + } + } + picked: + + /* + if(S7 == 8) { + gens[1] = {0,4,0,4}; + gens[0] = {2,0,2}; + } + else { + gens[0] = {0,4,0,4}; + gens[1] = {2,0,4,0,2}; + } + gens[2] = invertpath(gens[0]); + gens[3] = invertpath(gens[1]); + */ + + for(int i=0; i<4; i++) bttargets[i] = trace(cwt, gens[i]); + + celllister cl(cwt.c, 8+more, 1000000, NULL); + // recursively(cwt.c, NULL, [] (cell *c) { allcells.push_back(c); } ); + for(cell* c: cl.lst) bantar_note(c); + + for(cell *c: cl.lst) if(infos.count(c) && infos[c].gid == 0) + forCellEx(c2, c) if(infos.count(c2) && infos[c2].gid != 0) + c->bardir = NOBARRIERS; + + for(int it=0; it spdist; + +int curpart; + +/* bool hidebad(cell *c, const transmatrix& V) { + if(c->wparam != curpart && curpart < 4) return true; + return false; + } */ + +heptspin cth(cellwalker cw) { + return heptspin(cw.c->master, cw.spin, cw.mirrored); + } + +ld alphaof(hyperpoint h) { + return atan2(h[1], h[0]); + } + +struct bantar_special { + polytodraw *actual; + ld xpos, ypos; + }; + +#define ForInfos for(auto& cci: infos) + +void bantar_frame() { + setGLProjection(); + + ForInfos + cci.second.w = cci.second.c->wall, + cci.second.it = cci.second.c->item, + cci.second.mo = cci.second.c->monst, + cci.second.land = cci.second.c->land, + cci.second.c->wparam = cci.second.gid; + + calcparam(); + stereo::set_projection(0); + + vector subscr[4]; + + const int tmax = 2000; + int t = ticks % (5*tmax); + int tphase = t / tmax, tsub = t % tmax; + ld xdst, ydst; + + for(int i=0; i<4; i++) { + + ptds.clear(); + + cellwalker xcw; + ld part = 1; + if(i == 1) xcw = bttargets[0]; + else if(i == 2) xcw = bttargets[1], part = .5; + else if(i == 3) xcw = bttargets[3], part = .5; + else xcw = cwt; + + View = Id; + + transmatrix tView = actualV(cth(xcw), Id) * shmup::calc_relative_matrix(cwt.c, xcw.c) * inverse(actualV(cth(cwt), Id)); + + if(tphase < 2) part = 0; + else if(tphase == 2) + part = part * tsub / tmax; + + transmatrix itView = inverse(tView); + transmatrix z = rspintox(itView*C0) * xpush(hdist0(itView*C0) * part) * spintox(itView*C0); + transmatrix ful = rspintox(itView*C0) * xpush(hdist0(itView*C0) * tmax / tmax) * spintox(itView*C0); // rgpushxto0(itView*C0); + hyperpoint C1 = xpush(1) * C0; + ld bof = alphaof(tView * ful * C1); + z = z * spin(bof * part); + View = inverse(z); + + if(tphase == 0 && tsub > tmax/2) { + ld alpha = rand() % 10; + ld d = (rand() % 1000) / 20000. * (tsub-tmax/2) / (tmax/2); + View = spin(alpha) * xpush(d) * spin(-alpha); + } + + /* int phasemask = 3; + if(tphase == 0) phasemask = 0; + if(tphase == 4) phasemask = 2; */ + + ForInfos if(cci.second.gid == i) + cci.second.c->wall = cci.second.w, + cci.second.c->item = cci.second.it, + cci.second.c->monst = cci.second.mo, + cci.second.c->land = cci.second.land; + else + cci.second.c->wall = waInvisibleFloor, + cci.second.c->item = itNone, + cci.second.c->monst = moNone, + cci.second.c->land = laNone; + + mapeditor::drawplayer = cwt.c->wparam == i; + + switch(tphase) { + case 0: + xdst = ydst = 0; + curpart = 4; + break; + case 1: + xdst = ydst = .5 * tsub / tmax; + break; + case 2: + xdst = ydst = .5; + break; + case 3: + xdst = .5, ydst = .5 * (tmax-tsub) / tmax; + break; + case 4: + xdst = .5, ydst = 0; + break; + } + + /* ld xpos = (!(i&2)) ? xdst : -xdst; + ld ypos = (!(i&1)) ? ydst : -ydst; */ + + gmatrix.clear(); + + drawrec(viewctr, hsOrigin, cview()); + if(0) for(auto p: parent) if(gmatrix.count(p.first) && gmatrix.count(p.second) && infos[p.first].gid == i && infos[p.second].gid == i) + queueline(tC0(gmatrix[p.first]), tC0(gmatrix[p.second]), 0xFFFFFFFF, 2); + subscr[i] = ptds; + } + + map>> xptds; + for(int i=0; i<4; i++) for(auto& p: subscr[i]) + xptds[p.prio][i].push_back(p); + + for(auto& sm: xptds) for(auto& sm2: sm.second) { + int i = sm2.first; + ptds.clear(); + for(auto& p: sm2.second) ptds.push_back(p); + + vid.scale = .5; + vid.xposition = (!(i&2)) ? xdst : -xdst; + vid.yposition = (!(i&1)) ? ydst : -ydst; + calcparam(); + stereo::set_projection(0); + drawqueue(); + } + + ForInfos + cci.second.c->wall = cci.second.w, + cci.second.c->item = cci.second.it, + cci.second.c->monst = cci.second.mo, + cci.second.c->land = cci.second.land; + } + +void bantar_anim() { + + vid.aurastr = 0; + while(!quitmainloop) { + ticks = SDL_GetTicks(); + + bantar_frame(); + + SDL_GL_SwapBuffers(); + SDL_Event ev; + while(SDL_PollEvent(&ev)) handle_event(ev); + } + } + +bool bmap; + +bool bantar_stats() { + if(bmap) { + vid.linewidth *= (inHighQual ? 10 : 2); + for(auto p: parent) if(gmatrix.count(p.first) && gmatrix.count(p.second)) + queueline(tC0(gmatrix[p.first]), tC0(gmatrix[p.second]), 0x00FF00FF, 4); + + double x = hexvdist; + for(auto gm: gmatrix) for(cell *c: {gm.first}) + if(euclid || !pseudohept(c)) for(int t=0; ttype; t++) if(infos.count(c) && infos.count(c->mov[t]) && c->mov[t] && infos[c].gid != infos[c->mov[t]].gid) + if(euclid ? c->mov[t]mov[t] < c)) + queueline(gm.second * ddspin(c,t,-S7) * xpush(x) * C0, + gm.second * ddspin(c,t,+S7) * xpush(x) * C0, + 0xFF0000FF, 1); + vid.linewidth /= (inHighQual ? 10 : 2); + drawqueue(); + } + return false; + } + +void init_bantar() { + if(!on) { + stop_game(); + on = true; + start_game(); + } + } + +int readArgs() { + using namespace arg; + + if(0) ; + else if(argis("-bantar_anim")) { + PHASE(3); + init_bantar(); + peace::on = true; + airmap.clear(); + ForInfos if(cci.second.c->monst == moAirElemental) + cci.second.c->monst = moFireElemental; + bantar_anim(); + } + else if(argis("-bantar_test")) { + PHASE(3); + init_bantar(); + peace::on = true; + airmap.clear(); + ForInfos if(cci.second.c->monst == moAirElemental) + cci.second.c->monst = moFireElemental; + ForInfos if(cci.second.gid != 2) + cci.second.c->wall = waInvisibleFloor, + cci.second.c->item = itNone, + cci.second.c->monst = moNone, + cci.second.c->land = laNone; + airmap.clear(); + havewhat = 0; + } + else if(argis("-bantar_map")) { + init_bantar(); + bmap = true; + ForInfos { + int hsh = 0x202047; + for(int w: cci.second.way) hsh = (11301 * hsh + w * 37121) & 0x7F7F7F; + cci.second.c->landparam = hsh; + cci.second.c->land = laCanvas; + cci.second.c->wall = waNone; + cci.second.c->item = itNone; + cci.second.c->monst = moNone; + } + } + else if(argis("-btry")) { + shift(); notry = argi(); + } + else return 1; + return 0; + } + +auto hook = addHook(hooks_args, 100, readArgs) + + addHook(hooks_initgame, 100, bantar) + + addHook(hooks_frame, 100, bantar_stats); + + +} diff --git a/rogueviz-video.cpp b/rogueviz-video.cpp index c6afbbfa..417b7a51 100644 --- a/rogueviz-video.cpp +++ b/rogueviz-video.cpp @@ -159,14 +159,93 @@ struct storydata { int s; int e; const char *text; } story[] = { saveHighQualityShot(buf); } } -#endif -#if CAP_COMMANDLINE && CAP_SDL -int videoArgs() { - if(argis("-rvvideo")) { - shift(); rvvideo(args()); +string its05(int i) { char buf[64]; sprintf(buf, "%05d", i); return buf; } + +#define TSIZE 4096 + +void staircase_video(int from, int num, int step) { + resetbuffer rb; + renderbuffer rbuf(TSIZE, TSIZE, true); + stereo::mode = stereo::sODS; + + for(int i=from; i vx(vid.xres, TSIZE); + dynamicval vy(vid.yres, TSIZE); + dynamicval vxc(vid.xcenter, TSIZE/2); + dynamicval vyc(vid.ycenter, TSIZE/2); + stereo::set_viewport(0); + printf("draw scene\n"); + rug::drawRugScene(); + + IMAGESAVE(rbuf.render(), ("staircase/" + its05(i) + IMAGEEXT).c_str()); + printf("GL %5d/%5d\n", i, num); } + + rb.reset(); } + +#undef TSIZE + +#define TSIZE 2048 + +void bantar_record() { + resetbuffer rb; + renderbuffer rbuf(TSIZE, TSIZE, true); + + int fr = 0; + + for(int i=0; i < 10000; i += 33) { + if(i % 1000 == 999) i++; + ticks = i; + + rbuf.enable(); + vid.xres = vid.yres = TSIZE; + stereo::set_viewport(0); + banachtarski::bantar_frame(); + + IMAGESAVE(rbuf.render(), ("bantar/" + its05(fr) + IMAGEEXT).c_str()); + printf("GL %5d/%5d\n", i, 10000); + fr++; + } + + rb.reset(); + } +#undef TSIZE + +#if CAP_COMMANDLINE +int videoArgs() { + using namespace arg; + if(argis("-rvvideo")) { + shift(); rvvideo(arg::args()); + } + else if(argis("-staircase_video")) { + staircase_video(0, 128*30, 1); // goal: 168*30 + } + else if(argis("-bantar_record")) { + using namespace banachtarski; + PHASE(3); + peace::on = true; + airmap.clear(); + ForInfos if(cci.second.c->monst == moAirElemental) + cci.second.c->monst = moFireElemental; + bantar_record(); + } + else return 1; + return 0; + } +#endif #endif auto rv_hooks = addHook(hooks_args, 100, videoArgs); diff --git a/rogueviz.cpp b/rogueviz.cpp index 8c078a72..9ce6cfba 100644 --- a/rogueviz.cpp +++ b/rogueviz.cpp @@ -1825,4 +1825,6 @@ auto hooks = #include "rogueviz-kohonen.cpp" #include "rogueviz-staircase.cpp" +#include "rogueviz-banachtarski.cpp" +#include "rogueviz-video.cpp"