#include "../hyper.h" #include "rogueviz.h" // hyper -tol <filename.xml> -- 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<int> children; }; vector<treevertex> 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; i<isize(tol[at].children); i++) spos(tol[at].children[i], d+1); tol[at].epos = ++xpos; } void read(string fn) { fname = fn; init(RV_GRAPH | RV_COMPRESS_LABELS | RV_COLOR_TREE); tree_edge = add_edgetype("tree edge"); printf("Reading the tree of life...\n"); FILE *f = fopen(fname.c_str(), "rt"); if(!f) { printf("Failed to open tree file: %s\n", fname.c_str()); exit(1); } if(fgetc(f) != '(') { printf("Error: bad format\n"); exit(1); } readnode(f, -1); fclose(f); int N = isize(vdata); printf("N = %d\n", N); printf("Assigning spos/epos...\n"); spos(0, 0); xpos *= 6; printf("Creating vertices...\n"); for(int i=0; i<N; i++) { treevertex& lv = tol[i]; vertexdata& vd = vdata[i]; transmatrix h = spin((lv.spos + lv.epos) * M_PI / xpos) * xpush(-1.2 + (log(xpos) - log(lv.epos - lv.spos))); vd.special = false; vd.m = new shmup::monster; vd.m->pid = 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<isize(vdata); i++) { vertexdata& vd = vdata[i]; virtualRebase(vd.m); } printf("Clearing the TOL data...\n"); tol.clear(); storeall(); } int ah = arg::add3("-tree", [] { tree::read(arg::shift_args()); }) + addHook_rvslides(120, [] (string s, vector<tour::slide>& v) { if(s != "data") return; using namespace pres; v.push_back( tour::slide{"Tree of Life", 61, LEGAL::UNLIMITED | QUICKGEO, "Hyperbolic geometry is much better than the Euclidean geometry at visualizing large trees and other hierarchical structures. " "Here we visualize the data from the Tree of Life project.", roguevizslide('0', [] () { rogueviz::dftcolor = 0x206020FF; rogueviz::showlabels = true; gmatrix.clear(); drawthemap(); gmatrix0 = gmatrix; rogueviz::tree::read(RVPATH "treeoflife/tol.txt"); })} ); }); } }