From 0a54e569500ea510f360d9421dab80b3d00dfa81 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Tue, 23 Oct 2018 17:05:46 +0200 Subject: [PATCH] graph drawing in RogueViz --- hyper.h | 28 +++ rogueviz-graph.cpp | 446 +++++++++++++++++++++++++++++++++++++++++++++ rogueviz.cpp | 2 +- util.cpp | 97 ++++------ 4 files changed, 514 insertions(+), 59 deletions(-) create mode 100644 rogueviz-graph.cpp diff --git a/hyper.h b/hyper.h index 77843c0d..f30ffe9c 100644 --- a/hyper.h +++ b/hyper.h @@ -4239,5 +4239,33 @@ namespace ts { void generate_around(cell *c); int euclidAlt(short x, short y); +struct exp_parser { + string s; + int at; + exp_parser() { at = 0; } + + map extra_params; + + bool ok() { return at == isize(s); } + char next() { if(at == isize(s) || at == -1) return 0; else return s[at]; } + + bool eat(const char *c) { + int orig_at = at; + while(*c && *c == next()) at++, c++; + if(*c == 0) return true; + else at = orig_at; + return false; + } + + ld parse(int prio = 0); + + ld parsepar() { + ld res = parse(); + if(next() != ')') { at = -1; return res; } + at++; + return res; + } + }; + } diff --git a/rogueviz-graph.cpp b/rogueviz-graph.cpp new file mode 100644 index 00000000..d3238c12 --- /dev/null +++ b/rogueviz-graph.cpp @@ -0,0 +1,446 @@ +namespace rogueviz { + +namespace graph { + +bool graph_on; + +vector formula; +color_t graphcolor; + +transmatrix ts[3]; + +using namespace hyperpoint_vec; + +hyperpoint facingdir(array& a) { + hyperpoint tmp = (a[1]-a[0]) ^ (a[2]-a[0]); + tmp /= sqrt(tmp|tmp); + return tmp; + } + +vector> sideangles; + +cell *p0, *t0, *t1, *t2, *cc; + +bool snubon; + +hyperpoint cor; + +void kframe() { + if(snubon) { + queuestr(gmatrix[p0], 0.6, "P0", 0xFFFFFF, 1); + queuestr(gmatrix[cc], 0.6, "C", 0xFFFFFF, 1); + queuestr(gmatrix[t0], 0.6, "T0", 0xFFFFFF, 1); + queuestr(gmatrix[t1], 0.6, "T1", 0xFFFFFF, 1); + queuestr(gmatrix[t2], 0.6, "T2", 0xFFFFFF, 1); + }} + +hyperpoint xts0; +array mts; + +rug::rugpoint *pt(hyperpoint h, hyperpoint c, int id) { + auto r = rug::addRugpoint(C0, -1); + r->flat = h; + r->x1 = (1 + c[0]) / 16 + (id/8) / 8.; + r->y1 = (1 + c[1]) / 16 + (id%8) / 8.; + r->valid = true; + return r; + } + +hyperpoint inplane(array& a, hyperpoint line) { + hyperpoint mu = (a[1]-a[0]) ^ (a[2]-a[0]); + // (a[0] | mu) == (line * z | mu) + return line * (a[0] | mu) / (line | mu); + } + +transmatrix matrix2; + +#if CAP_TEXTURE +bool need_texture = true; +texture::texture_data tdata; // = texture::config.data; +#endif + +int global_v, global_w; + +void make_texture() { + #if CAP_TEXTURE + rug::renderonce = true; + need_texture = false; + tdata.whitetexture(); + int tw = tdata.twidth; + printf("tw = %d\n", tw); + int fw = tw / 4; + auto pix = [&] (int k, int x, int y) -> unsigned& { + return tdata.texture_pixels[y * tw + x + (k&3) * fw + (k>>2) * fw * tw]; + }; + for(int y=0; y 1) hyp = 1; + part(pix(0,x,y), p) = 255 * (1 * hyp + (0.5 + h[p]/2) * (1-hyp)); + } + + tdata.loadTextureGL(); + + rug::alternate_texture = tdata.textureid; + #endif + } + +void create_model(); + +void run_snub(int v, int w) { + snubon = false; + global_v = v; global_w = w; + printf("set geometry\n"); + stop_game(); autocheat = true; + int bonus; + if(w == 4 && v == 4) bonus = 12; + else if(w == 4 && v == 5) bonus = 7; + else if(w == 4 && v == 6) bonus = 4; + else if(w == 3 && v == 6) bonus = 12; + else if(w == 3 && v == 7) bonus = 8; + else if(w == 3 && v == 8) bonus = 7; + else if(w == 3 && v == 9) bonus = 6; + else bonus = 0; + gamerange_bonus = genrange_bonus = sightrange_bonus = bonus; + set_geometry(gArchimedean); + set_variation(eVariation::pure); + arcm::current.parse("("+its(v)+",3," + its(w) + ",3,3) (2,3)(1,0)(4)"); + specialland = laCanvas; + patterns::whichCanvas = 'A'; + // vid.wallmode = 1; + need_reset_geometry = true; + printf("start game\n"); + printf("distlimit = %d\n", base_distlimit); + precalc(); + printf("distlimit = %d\n", base_distlimit); + start_game(); + printf("ok\n"); + printf("allcells = %d\n", isize(currentmap->allcells())); + + sideangles.clear(); + printf("gamerange = %d\n", gamerange()); + printf("genrange = %d\n", getDistLimit() + genrange_bonus); + setdist(cwt.at, 7 - getDistLimit() - genrange_bonus, NULL); + bfs(); + + drawthemap(); + + if(euclid || sphere) for(cell *c: currentmap->allcells()) + gmatrix[c] = arcm::archimedean_gmatrix[c->master].second; + + cellwalker cw(currentmap->gamestart(), 0); + p0 = cw.at; + t0 = (cw - 1 + wstep).at; + t1 = (cw + wstep).at; + t2 = (cw + wstep + 1 + wstep - 1).at; + // p1 = (cw + wstep + 1 + wstep -1 + wstep).at; + cc = (cw - 1 + wstep - 1 + wstep).at; + + transmatrix rel = inverse(gmatrix[p0]); + + ts[0] = rel * gmatrix[t0] * ddspin(t0, (cw - 1 + wstep).spin); + ts[1] = rel * gmatrix[t1]; + ts[2] = rel * gmatrix[t2] * ddspin(t2, (cw + wstep + 1 + wstep - 1).spin); + + matrix2 = ts[2] * inverse(ts[0]); + + for(int i=0; i<3; i++) mts[i] = ts[i] * C0; + hyperpoint f = facingdir(mts); + + for(cell *c: currentmap->allcells()) { + int id = arcm::id_of(c->master); + if(among(id, 0, 1)) for(int d=0; d hts; + for(int i=0; i<3; i++) + hts[i] = T * ts[i] * C0; + // for(int i=0; i<3; i++) printf("%s ", display(hts[i])); + hyperpoint f1 = facingdir(hts); + + ld scalar = (f|f1); + ld alpha = (M_PI - acos(scalar)) * 180 / M_PI; + sideangles.emplace_back(alpha, T); + } + } + + vector sav; + for(auto p: sideangles) sav.push_back(p.first); + sort(sav.begin(), sav.end()); + + printf("sideangles "); for(int i=0; i<3; i++) printf("%lf ", double(sav[i])); printf("\n"); + + xts0 = tC0(ts[0]); + + printf("original %s\n", display(xts0)); + + cor = rel * gmatrix[cc] * C0; + + rug::reopen(); + for(auto p: rug::points) p->valid = true; + rug::good_shape = true; + + make_texture(); + + create_model(); + printf("points = %d tris = %d side = %d\n", isize(rug::points), isize(rug::triangles), isize(sideangles)); + rug::model_distance = euclid ? 4 : 2; + rug::rug_perspective = hyperbolic; + showstartmenu = false; + snubon = true; + rug::invert_depth = hyperbolic; + } + +void create_model() { + + int v = global_v; + rug::clear_model(); + ld x = (mousex - vid.xcenter + .0) / vid.xres; + ld y = (mousey - vid.ycenter + .0) / vid.yres; + + ld alpha = atan2(y, x); + ld h = hypot(x, y); + + hyperpoint chk = ts[0] * xspinpush0(alpha, h); + + mts[0] = chk; + mts[1] = spin(-2*M_PI/v) * chk; + mts[2] = matrix2 * chk; + + hyperpoint c[5]; + for(int i=0; i<5; i++) + c[i] = hpxy(sin(2 * i * M_PI/5), cos(2 * i * M_PI/5)); + + hyperpoint tria[5]; + tria[0] = mts[0]; + tria[1] = inplane(mts, C0); + tria[2] = mts[1]; + tria[3] = mts[2]; + tria[4] = inplane(mts, cor); + + hyperpoint ctr = Hypc; + for(int i=0; i<5; i++) ctr += tria[i]; + ctr = inplane(mts, ctr); + + transmatrix tester = spin(1.1) * xpush(1); + + int idh = 0; + + for(hyperpoint h: {ctr, tria[0], tria[1], tria[2], tria[3], tria[4], ctr}) { + int good1 = 0, good2 = 0; + // printf("%d: ", idh); + for(int i=0; i<5; i++) { + array testplane; + testplane[0] = tester * h; + testplane[1] = tester * tria[i]; + testplane[2] = tester * tria[(i+1)%5]; + hyperpoint f = facingdir(testplane); + // printf("%lf ", f[0]); + if(f[0] > -1e-6 || std::isnan(f[0])) good1++; + if(f[0] < +1e-6 || std::isnan(f[0])) good2++; + } + // printf("\n"); + if(good1 == 5 || good2 == 5) {ctr = h; break; } + idh++; + } + + // printf("idh = %d\n", idh); + + transmatrix t = hyperbolic ? rotmatrix(M_PI, 0, 2) * xpush(sin(ticks * M_PI / 8000.)) : rotmatrix(ticks * M_PI / 8000., 0, 2); + + hyperpoint hs = hyperbolic ? hpxyz(0,0,-1) : hpxyz(0,0,0); + + if(euclid) t = Id; + + int id = 0; + for(auto& p: sideangles) { + auto& T = p.second; + array hts; + auto cpt = pt(hs + t * T * ctr, C0, id); + + for(int s=0; s<5; s++) + hts[s] = pt(hs + t * T * tria[s], c[s], id); + + for(int s=0; s<5; s++) + rug::addTriangle(cpt, hts[s], hts[(s+1)%5]); + + id++; + if(!sphere) id %= global_v; + } + + } + +hyperpoint err = hpxyz(500,0,0); + +bool iserror(hyperpoint h) { return sqhypot2(h) > 10000 || isnan(h[0]) || isnan(h[1]) || isnan(h[2]) || isinf(h[0]) || isinf(h[1]) || isinf(h[2]); } + +hyperpoint find_point(ld t) { + exp_parser ep; + auto &dict = ep.extra_params; + dict["t"] = t; + dict["s"] = ticks / 1000.; + dict["phi"] = t * 2 * M_PI; + dict["x"] = tan(t * M_PI - M_PI/2); + for(auto& ff: formula) { + ep.s = ff; + string varname = ""; + ep.at = 0; + while(!among(ep.next(), '=', -1)) varname += ep.next(), ep.at++; + ep.at++; + ld x = ep.parse(); + if(!ep.ok()) return err; + dict[varname] = x; + } + if(!dict.count("y") && dict.count("r")) + return xspinpush0(dict["phi"], dict["r"]); + if(dict.count("z")) + return hpxyz(dict["x"], dict["y"], dict["z"]); + if(sphere && hypot(dict["x"], dict["y"]) > 1) + return err; + return hpxy(dict["x"], dict["y"]); + } + +hyperpoint gcurvestart = err; + +void xcurvepoint(hyperpoint h) { + curvepoint(cwtV * h); + if(iserror(gcurvestart)) + gcurvestart = h; + else if(sphere && intval(gcurvestart, h) > .1) { + queuecurve(graphcolor, 0, PPR::LINE); + curvepoint(cwtV * h); + gcurvestart = h; + } + } + +void finish() { + if(!iserror(gcurvestart)) { + queuecurve(graphcolor, 0, PPR::LINE); + gcurvestart = err; + } + } + +int small_limit = 6, big_limit = 20; + +void draw_to(ld t0, hyperpoint h0, ld t1, hyperpoint h1, int small = 0, int big = 0) { + if(iserror(h0) || iserror(h1) || intval(h0, h1) < .01) small++; + else small = 0; + if(small >= small_limit || big >= big_limit) { + xcurvepoint(h1); + return; + } + if(t1-t0 < 1e-6) { + finish(); + return; + } + ld t2 = (t0 + t1) / 2; + hyperpoint h2 = find_point(t2); + draw_to(t0, h0, t2, h2, small, big+1); + draw_to(t2, h2, t1, h1, small, big+1); + } + +int editpos = -1, editwhich = -1; + +void show_graph() { + cmode = sm::SIDE | sm::MAYDARK; + gamescreen(0); + dialog::init(XLAT("graph")); + for(int i=0; i isize(cs)) editpos = isize(cs); + cs.insert(editpos, "°"); + dialog::addItem(cs, '1'+i); + } + else { + dialog::addItem(formula[i], editwhich == -1 ? '1'+i : 0); + dialog::add_action([i] () { editwhich = i; editpos = isize(formula[i]); }); + } + } + + dialog::addBack(); + dialog::display(); + + keyhandler = [] (int sym, int uni) { + if(editwhich >= 0) { + string& edited = formula[editwhich]; + if(sym == SDLK_LEFT) editpos--; + else if(sym == SDLK_RIGHT) editpos++; + else if(uni == 8) { + if(editpos == 0) return; + edited.replace(editpos-1, 1, ""); + editpos--; + return; + } + else if(uni >= 32 && uni < 128) { + edited.insert(editpos, 1, uni); + editpos++; + return; + } + else if(doexiton(sym, uni)) + editwhich = -1; + } + else { + handlePanning(sym, uni); + dialog::handleNavigation(sym, uni); + // if(doexiton(sym, uni)) popScreen(); + } + }; + } + +bool frame() { + if(graphcolor) { + hyperpoint h0 = find_point(0); + hyperpoint h1 = find_point(1); + if(!iserror(h0)) xcurvepoint(h0); + draw_to(0, h0, 1, h1); + finish(); + } + return false; + } + +#if CAP_COMMANDLINE +int readArgs() { + using namespace arg; + + if(0) ; + else if(argis("-dgraph")) { + PHASE(3); + showstartmenu = false; + pushScreen(show_graph); + shift(); + while(args().find("=") != string::npos) { + formula.emplace_back(args()); + shift(); + } + graphcolor = arghex(); + } + else if(argis("-dgs")) { + small_limit = argi(); + } + else if(argis("-dgl")) { + big_limit = argi(); + } + else return 1; + return 0; + } +#endif + + +auto xhook = addHook(hooks_args, 100, readArgs) + + addHook(hooks_frame, 0, frame); + +} +} diff --git a/rogueviz.cpp b/rogueviz.cpp index c88363a7..3a4298d4 100644 --- a/rogueviz.cpp +++ b/rogueviz.cpp @@ -2276,4 +2276,4 @@ auto hooks = #include "rogueviz-banachtarski.cpp" #include "rogueviz-video.cpp" #include "rogueviz-pentagonal.cpp" - +#include "rogueviz-graph.cpp" diff --git a/util.cpp b/util.cpp index 6d1daf2e..027ab916 100644 --- a/util.cpp +++ b/util.cpp @@ -151,68 +151,49 @@ struct indenter { void doindent() { for(int i=0; i= '0' && c <= '9') || among(c, 'e', 'p', '.')) - number += c, at++; - else break; - } - if(number == "e") res = exp(1); - else if(number == "p" || number == "pi") res = M_PI; - else if(number == "" && next() == '-') res = 0, prio = 0; - else if(number == "") at = -1; - else if(number[0] >= 'a' && number[0] <= 'z') at = -1; - else { std::stringstream ss; res = 0; ss << number; ss >> res; } - } +ld exp_parser::parse(int prio) { + ld res; + if(eat("sin(")) res = sin(parsepar()); + else if(eat("cos(")) res = cos(parsepar()); + else if(eat("sinh(")) res = sinh(parsepar()); + else if(eat("cosh(")) res = cosh(parsepar()); + else if(eat("asin(")) res = asin(parsepar()); + else if(eat("acos(")) res = acos(parsepar()); + else if(eat("asinh(")) res = asinh(parsepar()); + else if(eat("acosh(")) res = acosh(parsepar()); + else if(eat("exp(")) res = exp(parsepar()); + else if(eat("log(")) res = log(parsepar()); + else if(eat("tan(")) res = tan(parsepar()); + else if(eat("tanh(")) res = tanh(parsepar()); + else if(eat("atan(")) res = atan(parsepar()); + else if(eat("atanh(")) res = atanh(parsepar()); + else if(next() == '(') at++, res = parsepar(); + else { + string number; while(true) { - if(next() == '+' && prio == 0) at++, res = res + parse(1); - else if(next() == '-' && prio == 0) at++, res = res - parse(1); - else if(next() == '*' && prio <= 1) at++, res = res * parse(2); - else if(next() == '/' && prio <= 1) at++, res = res / parse(2); - else if(next() == '^') at++, res = pow(res, parse(3)); + char c = next(); + if((c >= '0' && c <= '9') || c == '.' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') + number += c, at++; else break; } - return res; + if(number == "e") res = exp(1); + else if(number == "p" || number == "pi") res = M_PI; + else if(number == "" && next() == '-') res = 0, prio = 0; + else if(number == "") at = -1; + else if(extra_params.count(number)) res = extra_params[number]; + else if(number[0] >= 'a' && number[0] <= 'z') at = -1; + else { std::stringstream ss; res = 0; ss << number; ss >> res; } } - - ld parsepar() { - ld res = parse(); - if(next() != ')') { at = -1; return res; } - at++; - return res; - } - }; + while(true) { + if(next() == '+' && prio == 0) at++, res = res + parse(1); + else if(next() == '-' && prio == 0) at++, res = res - parse(1); + else if(next() == '*' && prio <= 1) at++, res = res * parse(2); + else if(next() == '/' && prio <= 1) at++, res = res / parse(2); + else if(next() == '^') at++, res = pow(res, parse(3)); + else break; + } + return res; + } ld parseld(const string& s) { exp_parser ep;