diff --git a/rogueviz/collatz.cpp b/rogueviz/collatz.cpp new file mode 100644 index 00000000..02fc64a0 --- /dev/null +++ b/rogueviz/collatz.cpp @@ -0,0 +1,336 @@ +#include "../hyper.h" +#include "rogueviz.h" + +namespace rogueviz { + +namespace collatz { + + double s2, s3, p2, p3; + double cshift = -1; + + transmatrix T2, T3; + + edgetype *collatz1, *collatz2; + + void start() { + init(); kind = kCollatz; + collatz1 = add_edgetype("1"); + collatz2 = add_edgetype("2"); + vdata.resize(1); + vertexdata& vd = vdata[0]; + createViz(0, cwt.at, xpush(cshift)); + virtualRebase(vd.m); + vd.cp = dftcolor; + vd.data = 0; + addedge(0, 0, 1, false, collatz::collatz1); + vd.name = "1"; + storeall(); + + T2 = spin(collatz::s2) * xpush(collatz::p2); + T3 = spin(collatz::s3) * xpush(collatz::p3); + } + + void lookup(long long reached, int bits) { + while(reached < (1ll< seq; + while(reached>1) { + seq.push_back(llts(reached)); + if(reached&1) reached += (reached>>1)+1; + else reached >>= 1; + } + // seq.push_back("1"); + reverse(seq.begin(), seq.end()); + + int id = 0; + int next = 0; + + int steps = 0; + while(true) { + steps++; + if(std::isnan(View[0][0])) exit(1); + shmup::turn(100); + drawthemap(); + centerpc(100); optimizeview(); + fixmatrix(View); + bfs(); setdist(cwt.at, 7 - getDistLimit() - genrange_bonus, NULL); + vertexdata& vd = vdata[id]; + for(int e=0; ebase; + if(shmup::on) shmup::pc[0]->base = cwt.at; + if(next == isize(seq)) goto found; + } + } + } + + found: + printf("steps = %d\n", steps); + } + + void act(vertexdata& vd, cell *c, shmup::monster *m, int i) { + if(c->cpdist > 7 && euclid) ; + else if(vd.data == 2) { + // doubler vertex + string s = vd.name; + colorpair cp = vd.cp; + vd.data = 20; + int i0 = isize(vdata); + vdata.resize(i0+1); + vertexdata& vdn = vdata[i0]; + createViz(i0, m->base, m->at * collatz::T2); + + virtualRebase(vdn.m); + vdn.cp = perturb(cp); + vdn.data = 0; + addedge(i, i0, 1, false, collatz::collatz1); + vdn.m->store(); + int carry = 0; + string s2 = s; + for(int i=isize(s2)-1; i>=0; i--) { + int x = 2*(s2[i] - '0') + carry; + carry = x>=10; + if(carry) x-=10; + s2[i] = '0'+x; + } + if(carry) s2 = "1" + s2; + vdn.name = s2; + + int m3 = 0; + for(int i=0; ibase, m->at * collatz::T3); + virtualRebase(vdn.m); + vdn.cp = perturb(cp); + vdn.data = 0; + addedge(i, i0+1, 1, false, collatz::collatz2); + vdn.m->store(); + int carry = -1; + string s2 = s; + for(int i=isize(s2)-1; i>=0; i--) { + carry += 2 * (s2[i] - '0'); + int ncarry = 0; + while(carry % 3) carry += 10, ncarry--; + if(carry >= 30) carry -= 30, ncarry += 3; + s2[i] = '0'+carry/3; + carry = ncarry; + } + if(s2[0] == '0') s2 = s2.substr(1); + vdn.name = s2; + vdn.cp = perturb(vdn.cp); + } + } + else if(vd.data < 2) { + vd.data++; + fixmatrix(vd.m->at); + } + } + +#if CAP_SHOT + +// see: https://www.youtube.com/watch?v=4Vu3F95jpQ4&t=6s (Collatz) +void collatz_video(const string &fname) { + if(kind == kCollatz) { + sightrange_bonus = 3; + genrange_bonus = 3; + dronemode = true; vid.camera_angle = -45; rog3 = true; patterns::whichShape = '8'; + vid.aurastr = 512; + + collatz::lookup(763, 60); + + history::create_playerpath(), models::rotation = 1; + // pmodel = mdBand; + +#define STORYCOUNT 24 +#define T(m,ss) (60*24*(m)+24*(ss)) +#define FRAMECOUNT T(4,55) + +printf("framecount = %d\n", FRAMECOUNT); + +struct storydata { int s; int e; const char *text; } story[] = { + {T(0,14), T(0,17), "I am flying above a tree of numbers."}, + {T(0,17), T(0,20), "It starts with the number 1."}, + {T(0,20), T(0,23), "Each number n branches left to 2n."}, + {T(0,23), T(0,28), "And it branches right to (2n-1)/3 if possible."}, + + {T(1, 8), T(1,11), "What I am flying above is not a plane."}, + {T(1,11), T(1,14), "It is not a sphere either."}, + {T(1,14), T(1,17), "To be honest, the space I live in..."}, + {T(1,17), T(1,20), "...is not even Euclidean."}, + + {T(2,12), T(2,15), "Look, angles of a triangle add up to..."}, + {T(2,15), T(2,18), "...less than 180 degrees in this world."}, + {T(2,18), T(2,21), "6/7 of 180 degrees, to be exact."}, + {T(2,21), T(2,24), "Do you see the regular heptagons?"}, + {T(2,36), T(2,42), "And all these lines are straight."}, + + {T(3, 8), T(3,11), "Lots of space in my world."}, + {T(3,11), T(3,14), "In 105 steps from the root..."}, + {T(3,14), T(3,17), "...there are trillions of numbers."}, + {T(3,17), T(3,20), "That would not fit in your world."}, + + {T(4,0), T(4,3), "Is every positive number somewhere in the tree?"}, + {T(4,3), T(4,6), "Your mathematicians do not know this yet."}, + {T(4,6), T(4,10), "Will you find the answer?"}, + + {T(4,44), T(4,54), "music: Ambient Flow, by Indjenuity"}, + + {T(2,6), T(2,27), "@triangles"}, + {T(2,27), T(2,42), "@network"}, + + {0, T(0,7), "@fi"}, + {T(4,48), T(4,55), "@fo"}, + + {0,0,NULL} + }; + + int drawtris=0, drawnet=0; + + for(int i=0; i= story[j].s && i <= story[j].e) { + if(story[j].text[0] != '@') + caption = story[j].text; + else if(story[j].text[1] == 't') + dt = true; + else if(story[j].text[1] == 'n') + dn = true; + else if(story[j].text[2] == 'i') + fade = 255 * (i - story[j].s) / (story[j].e-story[j].s); + else if(story[j].text[2] == 'o') + fade = 255 * (story[j].e - i) / (story[j].e-story[j].s); + } + + if(dt && drawtris < 255) drawtris++; + else if(drawtris && !dt) drawtris--; + + linepatterns::patZebraTriangles.color = 0x40FF4000 + drawtris; + + if(dn && drawnet < 255) drawnet++; + else if(drawnet && !dn) drawnet--; + + linepatterns::patZebraLines.color = 0xFF000000 + drawnet; + + vid.grid = drawnet; + + history::phase = 1 + (isize(history::v)-3) * i * .95 / FRAMECOUNT; + history::movetophase(); + + char buf[500]; + snprintf(buf, 500, fname.c_str(), i); + + if(i == 0) drawthemap(); + shmup::turn(100); + printf("%s\n", buf); + shot::shoty = 1080; shot::shotx = 1920; + shot::caption = caption; + shot::fade = fade; + shot::take(buf); + } + + return; + } + } +#endif + +string its05(int i) { char buf[64]; sprintf(buf, "%05d", i); return buf; } + +int readArgs() { +#if CAP_COMMANDLINE + using namespace arg; + + if(0) ; + + else if(argis("-collatz")) { + PHASE(3); + using namespace collatz; + shift(); sscanf(argcs(), "%lf,%lf,%lf,%lf", &s2, &p2, &s3, &p3); + start(); + } + + else if(argis("-collatz-go")) { + if(kind != kCollatz) { printf("not in Collatz\n"); throw hr_exception(); } + shift(); int i = argi(); shift(); int j = argi(); + if(i <= 0) i = 763; + if(j < 0 || j > 61) j = 61; + collatz::lookup(i, j); + } + + else if(argis("-collatz3")) { + PHASE(3); + using namespace collatz; + s2 = p2 = s3 = p3 = 0; + start(); + transmatrix *T = &T2; + while(true) { + lshift(); + if(arg::nomore()) break; + else if(argis("fd")) { shift(); *T = *T * xpush(argf()); } + else if(argcs()[0] == 't') { int x = dimid(argcs()[1]); int y = dimid(argcs()[2]); shift(); *T = *T * hr::cspin(x, y, argf()); } + else if(argis("/")) { if(T == &T2) T = &T3; else break; } + else break; + } + unshift(); + } + + #if CAP_SHOT + else if(argis("-rvvideo") && kind == kCollatz) { + shift(); collatz_video(arg::args()); + } + #endif + + else if(argis("-cshift")) { + shift_arg_formula(collatz::cshift); + } + else return 1; +#endif + return 0; + } + +int ah = addHook(hooks_args, 100, readArgs) + + addHook(rvtour::hooks_build_rvtour, 100, [] (vector& v) { + using namespace tour; + v.push_back( + tour::slide{"Collatz conjecture", 51, LEGAL::UNLIMITED | QUICKGEO, + "The following slide is a visualization of the Collatz conjecture. " + "Press '5' for a spiral rendering of the Collatz conjecture visualization.\n\n" + "Note that this, and many other RogueViz visualizations, have " + "Euclidean versions (press ESC).\n", + rvtour::roguevizslide('d', [] () { + rogueviz::dftcolor = 0x206020FF; + + int fac = euclid ? 2 : 1; + + rogueviz::collatz::s2 = .3; + rogueviz::collatz::p2 = .5 * fac; + rogueviz::collatz::s3 = -.4; + rogueviz::collatz::p3 = .4 * fac; + + rogueviz::showlabels = true; + + rogueviz::on = true; + gmatrix.clear(); + drawthemap(); + gmatrix0 = gmatrix; + + rogueviz::collatz::start(); + }) + }); + }); + +EX } + +} diff --git a/rogueviz/sag.cpp b/rogueviz/sag.cpp new file mode 100644 index 00000000..f39b8b09 --- /dev/null +++ b/rogueviz/sag.cpp @@ -0,0 +1,628 @@ +#include "../hyper.h" +#include "rogueviz.h" + +// SAG visualizer (e.g. Reddit roguelikes, GitHub languages) +//----------------------------------------------------------- + +// see: https://www.youtube.com/watch?v=mDG3_f8R2Ns (SAG boardgames) +// see: https://www.youtube.com/watch?v=WSyygk_3j9o (SAG roguelikes) +// see: https://www.youtube.com/watch?v=HWQkDkeEUeM (SAG programming languages) + +namespace rogueviz { + +namespace sag { + + int sagpar = 0; + + enum eSagmode { sagOff, sagHC, sagSA }; + + eSagmode sagmode; // 0 - off, 1 - hillclimbing, 2 - SA + + const char *sagmodes[3] = {"off", "HC", "SA"}; + + ld temperature = -4; + const int INSNAKE = 117; + int numsnake; + const char *loadfname; + + #define MAXSNAKETAB 1000 + int sdist[MAXSNAKETAB][MAXSNAKETAB]; + int insnaketab = 0; + + vector snakecells; + vector snakefirst, snakelast; + vector snakenode; + vector snakeid; + vector lpbak; + vector wpbak; + + bool snake_enabled; + + void setsnake(cellwalker& cw, int i) { + lpbak[i] = cw.at->landparam; + wpbak[i] = cw.at->wparam; + cw.at->landparam = i; cw.at->wparam = INSNAKE; + // cw.at->monst = moWormtail; cw.at->mondir = cw.spin; + snakecells[i] = cw.at; + } + + void snakeswitch() { + for(int i=0; ilandparam; c->landparam = x; + x = wpbak[i]; wpbak[i] = c->wparam; c->wparam = x; + } + snake_enabled = !snake_enabled; + } + + void enable_snake() { if(!snake_enabled) snakeswitch(); } + + void disable_snake() { if(snake_enabled) snakeswitch(); } + + int snakedist(int i, int j) { + if(i < insnaketab && j < insnaketab) return sdist[i][j]; + if(bounded) return celldistance(snakecells[i], snakecells[j]); + int i0 = i, i1 = i, j0 = j, j1 = j; + int cost = 0; + // intersect + while(true) { + if(j0 > i1+1) { j0 = snakefirst[j0], j1 = snakelast[j1]; cost++; } + else if(i0 > j1+1) { i0 = snakefirst[i0], i1 = snakelast[i1]; cost++; } + else if(j1+1 == i0) return cost+1; + else if(i1+1 == j0) return cost+1; + else return cost; + } + } + + void initSnake(int n) { + if(bounded) n = isize(currentmap->allcells()); + numsnake = n; + snakecells.resize(numsnake); + snakefirst.resize(numsnake); + snakelast.resize(numsnake); + snakenode.resize(numsnake); + lpbak.resize(numsnake); + wpbak.resize(numsnake); + if(bounded) { + for(int i=0; iallcells()[i], 0); + setsnake(cw, i); + } + } + else { + cellwalker cw = cwt; + setsnake(cw, 0); + cw += wstep; + setsnake(cw, 1); + for(int i=2; i<=numsnake; i++) { + if(i == numsnake && sphere) break; + cw += wstep; + snakefirst[i-1] = cw.at->landparam; + while(cw.at->wparam == INSNAKE) { + snakelast[i-1] = cw.at->landparam; + cw = cw + wstep + 1 + wstep; + } + if(i == numsnake) break; + setsnake(cw, i); cw += 1; + } + } + int stab = min(numsnake, MAXSNAKETAB); + for(int i=0; iweight2; + } + /* cell *c = snakecells[id]; + for(int i=0; itype; i++) { + cell *c2 = c->move(i); + if(c2 && c2->wparam == INSNAKE && snakenode[c2->landparam] >= 0) + cost += 100; + } */ + return cost; + } + + // std::mt19937 los; + + bool infullsa; + + double cost; + int N; + + vector chgs; + + edgetype *sag_edge; + + void forgetedges(int id) { + for(int i=0; iorig = NULL; + } + + bool chance(double p) { + p *= double(hrngen.max()) + 1; + auto l = hrngen(); + auto pv = (decltype(l)) p; + if(l < pv) return true; + if(l == pv) return chance(p-pv); + return false; + } + + void saiter() { + aiter: + + int t1 = hrand(N); + int sid1 = snakeid[t1]; + + int sid2; + + int s = hrand(6); + + if(s == 3) s = 2; + if(s == 4) s = 5; + + if((sagpar&1) && (s == 2 || s == 3 || s == 4)) return; + + if(s == 5) sid2 = hrand(numsnake); + + else { + cell *c; + if(s>=2 && isize(vdata[t1].edges)) c = snakecells[snakeid[hrand(isize(vdata[t1].edges))]]; + else c = snakecells[sid1]; + + int it = s<2 ? (s+1) : s-2; + for(int ii=0; iitype); + c = c->move(d); + if(!c) goto aiter; + if(c->wparam != INSNAKE) goto aiter; + } + sid2 = c->landparam; + } + int t2 = snakenode[sid2]; + + snakenode[sid1] = -1; snakeid[t1] = -1; + snakenode[sid2] = -1; if(t2 >= 0) snakeid[t2] = -1; + + double change = + costat(t1,sid2) + costat(t2,sid1) - costat(t1,sid1) - costat(t2,sid2); + + snakenode[sid1] = t1; snakeid[t1] = sid1; + snakenode[sid2] = t2; if(t2 >= 0) snakeid[t2] = sid2; + + if(change < 0) chgs.push_back(-change); + + if(change > 0 && (sagmode == sagHC || !chance(exp(-change * exp(-temperature))))) return; + + snakenode[sid1] = t2; snakenode[sid2] = t1; + snakeid[t1] = sid2; if(t2 >= 0) snakeid[t2] = sid1; + if(vdata[t1].m) vdata[t1].m->base = snakecells[sid2]; + if(t2 >= 0 && vdata[t2].m) vdata[t2].m->base = snakecells[sid1]; + cost += 2*change; + + if(t1 >= 0) forgetedges(t1); + if(t2 >= 0) forgetedges(t2); + } + + void organize() { + for(int i=0; i freenodes; + for(int i=0; i= numsnake || err < 1) sid = -1; + if(!labeler.count(lab)) { + printf("unknown vertex: %s\n", lab.c_str()); + } + else { + int id = getid(lab); + snakeid[id] = sid; + } + } + afterload: + if(sf) fclose(sf); + + organize(); + for(int i=0; ibase = snakecells[sag::snakeid[i]]; + forgetedges(i); + } + + shmup::fixStorage(); + } + + vector sagedges; + + /* bool totcmp(int i, int j) { + return totwei[i] > totwei[j]; + } */ + + int ipturn = 100; + int numiter = 0; + + int hightemp = 10; + int lowtemp = -15; + + void dofullsa(int satime) { + sagmode = sagSA; + enable_snake(); + int t1 = SDL_GetTicks(); + + while(true) { + int t2 = SDL_GetTicks(); + double d = (t2-t1) / (1000. * satime); + if(d > 1) break; + temperature = hightemp - (d*(hightemp-lowtemp)); + chgs.clear(); + for(int i=0; i<50000; i++) { + numiter++; + sag::saiter(); + } + DEBB(DF_LOG, (format("it %8d temp %6.4f [1/e at %13.6f] cost = %f ", + numiter, double(sag::temperature), (double) exp(sag::temperature), + double(sag::cost)))); + + sort(chgs.begin(), chgs.end()); + int cc = chgs.size() - 1; + DEBB(DF_LOG, (format("%9.4f .. %9.4f .. %9.4f .. %9.4f .. %9.4f\n", + double(chgs[0]), double(chgs[cc/4]), double(chgs[cc/2]), double(chgs[cc*3/4]), double(chgs[cc])))); + fflush(stdout); + } + + temperature = -5; + disable_snake(); + sagmode = sagOff; + } + + void iterate() { + if(!sagmode) return; + int t1 = SDL_GetTicks(); + enable_snake(); + for(int i=0; i 200) ipturn /= 2; + else ipturn = ipturn * 100 / t; + DEBB(DF_LOG, ("it %8d temp %6.4f [2:%8.6f,10:%8.6f,50:%8.6f] cost = %f\n", + numiter, double(sag::temperature), + (double) exp(-2 * exp(-sag::temperature)), + (double) exp(-10 * exp(-sag::temperature)), + (double) exp(-50 * exp(-sag::temperature)), + (double) sag::cost)); + } + + void savesnake(const string& fname) { + FILE *f = fopen(fname.c_str(), "wt"); + for(int i=0; i= sag_edge->visible_from) + pedge[snakedist(snakeid[ei.i], snakeid[ei.j])]++; + } + + for(int d=0; d<30; d++) + if(indist[d]) + printf("%2d: %7d/%7d %7.3lf\n", + d, pedge[d], indist[d], double(pedge[d] * 100. / indist[d])); + + ld loglik = 0; + for(int d=0; d<30; d++) { + int p = pedge[d], pq = indist[d]; + int q = pq - p; + if(p && q) + loglik += p * log(p) + q * log(q) - pq * log(pq); + } + + println(hlog, "loglikelihood = ", fts(loglik)); + } + + void readsag(const char *fname) { + maxweight = 0; + sag_edge = add_edgetype("SAG edge"); + fhstream f(fname, "rt"); + if(!f.f) { printf("Failed to open SAG file: %s\n", fname); exit(1); } + // while(fgetc(f) != 10 && fgetc(f) != 13 && !feof(f)) ; + while(!feof(f.f)) { + string l1, l2; + while(true) { + int c = fgetc(f.f); + if(c == EOF) return; + else if(c == ';') break; + else if(c == 10 || c == 13 || c == 32 || c == 9) ; + else l1 += c; + } + while(true) { + int c = fgetc(f.f); + if(c == EOF) return; + else if(c == ';') break; + else if(c == 10 || c == 13 || c == 32 || c == 9) ; + else l2 += c; + } + ld wei; + if(!scan(f, wei)) continue; + edgeinfo ei(sag_edge); + ei.i = getid(l1); + ei.j = getid(l2); + ei.weight = wei; + sagedges.push_back(ei); + } + } + + ld edgepower=1, edgemul=1; + + void read(string fn) { + fname = fn; + init(); kind = kSAG; + temperature = 0; sagmode = sagOff; + readsag(fname.c_str()); + + N = isize(vdata); + // totwei.resize(N); + // for(int i=0; i= maxwei[ei.i] / 5 || ei.weight >= maxwei[ei.j] / 5); + + ei.weight2 = pow((double) ei.weight, (double) edgepower) * edgemul; + // LANG:: pow(ei.weight, .4) / 50; + + // ei.weight2 = 0; int w = ei.weight; while(w) { w >>= 1; ei.weight2++; } + /* if(totwei[ei.i] <= 0 || totwei[ei.j] <= 0) { + printf("BAD TOTWEI\n"); + exit(1); + } + ei.weight2 = 3 * ( + sqrt(ei.weight * 1. / totwei[ei.i]) * log(totwei[ei.i]) * log(totwei[ei.i]) + + sqrt(ei.weight * 1. / totwei[ei.j]) * log(totwei[ei.j]) * log(totwei[ei.j])); */ + // printf("%f\n", ei.weight2); + addedge0(ei.i, ei.j, &ei); + } + + initSnake(N*2); + printf("numsnake = %d\n", numsnake); + if(numsnake < N) { + printf("Error: snake does not fit\n"); + exit(1); + } + snakeid.resize(N); + for(int i=0; i + else if(argis("-fullsa")) { + shift(); sag::dofullsa(argi()); + } +// (5) save the positioning + else if(argis("-gsave")) { + PHASE(3); shift(); sag::savesnake(args()); + } +// (6) output loglikelihood + else if(argis("-lik")) { + sag::loglik(); + } + else return 1; +#endif + return 0; + } + +bool turn(int delta) { + if(!on) return false; + if(kind == kSAG) sag::iterate(), timetowait = 0; + return false; + // shmup::pc[0]->rebase(); + } + +int ah = addHook(hooks_args, 100, readArgs) + + addHook(shmup::hooks_turn, 100, turn) + + addHook(rogueviz::hooks_close, 100, [] { sag::sagedges.clear(); }) + + addHook(rogueviz::hooks_rvmenu, 100, [] { + if(kind != kSAG) return; + dialog::addSelItem(XLAT("temperature"), fts(sag::temperature), 't'); + dialog::add_action([] { + dialog::editNumber(sag::temperature, sag::lowtemp, sag::hightemp, 1, 0, XLAT("temperature"), ""); + }); + dialog::addSelItem(XLAT("SAG mode"), sag::sagmodes[sag::sagmode], 'm'); + dialog::add_action([] { sag::sagmode = sag::eSagmode( (1+sag::sagmode) % 3 ); }); + }) + + addHook(rvtour::hooks_build_rvtour, 100, [] (vector& v) { + using namespace rvtour; + v.push_back( + slide{"Roguelikes", 63, LEGAL::UNLIMITED | QUICKGEO, + "A visualization of roguelikes, based on discussion on /r/reddit. " + "See: http://www.roguetemple.com/z/hyper/reddit.php", + roguevizslide('0', [] () { + rogueviz::dftcolor = 0x282828FF; + + rogueviz::showlabels = true; + part(rogueviz::default_edgetype.color, 0) = 181; + rogueviz::sag::edgepower = 1; + rogueviz::sag::edgemul = 1; + + rogueviz::on = true; + gmatrix.clear(); + drawthemap(); + gmatrix0 = gmatrix; + + rogueviz::sag::read(RVPATH "roguelikes/edges.csv"); + rogueviz::readcolor(RVPATH "roguelikes/color.csv"); + rogueviz::sag::loadsnake(RVPATH "roguelikes/" + cname()); + }) + } + ); + v.push_back(slide {"Programming languages of GitHub", 64, LEGAL::UNLIMITED | QUICKGEO, + "A visualization of programming languages.", + roguevizslide('0', [] () { + rogueviz::dftcolor = 0x282828FF; + + rogueviz::showlabels = true; + part(rogueviz::default_edgetype.color, 0) = 128; + rogueviz::sag::edgepower = .4; + rogueviz::sag::edgemul = .02; + + rogueviz::on = true; + gmatrix.clear(); + drawthemap(); + gmatrix0 = gmatrix; + + rogueviz::sag::read(RVPATH "lang/edges.csv"); + rogueviz::readcolor(RVPATH "lang/color.csv"); + rogueviz::sag::loadsnake(RVPATH "lang/" + cname()); + if(euclid) rogueviz::legend.clear(); + }) + }); + + v.push_back(slide {"Boardgames", 62, LEGAL::UNLIMITED | QUICKGEO, + "A visualization of board games, based on discussions on Reddit.", + roguevizslide('0', [] () { + rogueviz::dftcolor = 0x282828FF; + + rogueviz::showlabels = true; + part(rogueviz::default_edgetype.color, 0) = 157; + rogueviz::sag::edgepower = 1; + rogueviz::sag::edgemul = 1; + + rogueviz::on = true; + gmatrix.clear(); + drawthemap(); + gmatrix0 = gmatrix; + + rogueviz::sag::read(RVPATH "boardgames/edges.csv"); + rogueviz::readcolor(RVPATH "boardgames/color.csv"); + rogueviz::sag::loadsnake(RVPATH "boardgames/" + cname()); + }) + }); + + }); + +EX } + +} diff --git a/rogueviz/tree.cpp b/rogueviz/tree.cpp new file mode 100644 index 00000000..215229c3 --- /dev/null +++ b/rogueviz/tree.cpp @@ -0,0 +1,126 @@ +#include "../hyper.h" +#include "rogueviz.h" + +// hyper -tol -- visualize the tree of life, +// based on a XML dump from https://tree.opentreeoflife.org/ + +namespace rogueviz { + +namespace tree { + + edgetype *tree_edge; + + struct treevertex { + int origid; + int parent; + int depth; + int spos, epos; + vector children; + }; + + vector tol; + + void child(int pid, int id) { + if(isize(tol) <= id) tol.resize(id+1); + + treevertex& v = tol[id]; + v.parent = pid; + tol.push_back(v); + if(pid >= 0) tol[pid].children.push_back(id); + } + + void readnode(FILE *f, int pid) { + string lab = ""; + while(true) { + int c = fgetc(f); + if(c == EOF) { fprintf(stderr, "Ended prematurely\n"); exit(1); } + if(c == ',') break; + if(c == ')') { int id = getnewid(lab); child(pid, id); return; } + lab += c; + } + int id = getnewid(lab); + child(pid, id); + while(true) { + int c = fgetc(f); +// printf("c=%c at %d/%d\n", c, pid, id); + if(c == EOF) { fprintf(stderr, "Ended prematurely\n"); exit(1); } + if(c == ' ' || c == 10 || c == 13 || c == 9 || c == ',') continue; + else if(c == '(') readnode(f, id); + else if(c == ')') break; + } + } + + int xpos; + void spos(int at, int d) { + tol[at].spos = xpos++; + tol[at].depth = d; + for(int i=0; ipid = i; + vd.data = lv.parent; + createViz(i, cwt.at, h); + vd.cp = dftcolor; + + if(tol[i].parent >= 0) + addedge(i, tol[i].parent, 1, true, tree_edge); + } + + for(int i=0; i