graph drawing in RogueViz

This commit is contained in:
Zeno Rogue 2018-10-23 17:05:46 +02:00
parent 6b47221a64
commit 0a54e56950
4 changed files with 514 additions and 59 deletions

28
hyper.h
View File

@ -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<string, ld> 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;
}
};
}

446
rogueviz-graph.cpp Normal file
View File

@ -0,0 +1,446 @@
namespace rogueviz {
namespace graph {
bool graph_on;
vector<string> formula;
color_t graphcolor;
transmatrix ts[3];
using namespace hyperpoint_vec;
hyperpoint facingdir(array<hyperpoint,3>& a) {
hyperpoint tmp = (a[1]-a[0]) ^ (a[2]-a[0]);
tmp /= sqrt(tmp|tmp);
return tmp;
}
vector<pair<ld, transmatrix>> 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<hyperpoint, 3> 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<hyperpoint, 3>& 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<tw; y++)
for(int x=0; x<tw; x++)
for(int p=0; p<3; p++) {
int ax = x / (tw/8);
int ay = y / (tw/8);
int bx = x % (tw/8);
int by = y % (tw/8);
int id = ax * 8 + ay;
hyperpoint h = sideangles[id % isize(sideangles)].second * xts0;
if(!sphere) {
hyperpoint ehs[7] = {hpxyz(0,-1,-1), hpxyz(0,0,-1), hpxyz(-1,0,-1), hpxyz(-1,0,0), hpxyz(-1,-1,0), hpxyz(0,-1,0), hpxyz(0,-1,-1)};
ld idx = (id % global_v) * 6. / global_v;
h = ehs[int(idx)] * (1-(idx-int(idx))) + ehs[int(idx)+1] * (idx-int(idx));
}
ld hyp = hypot(bx-tw/16, by-tw/16) / (tw/16);
if(hyp > 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<v; d++) {
transmatrix T = rel * ggmatrix(c) * spin(2*M_PI*d/v);
array<hyperpoint,3> 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<double> 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<hyperpoint, 3> 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<rug::rugpoint*,5> 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(formula); i++) {
if(editwhich == i) {
string cs = formula[i];
if(editpos < 0) editpos = 0;
if(editpos > 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);
}
}

View File

@ -2276,4 +2276,4 @@ auto hooks =
#include "rogueviz-banachtarski.cpp"
#include "rogueviz-video.cpp"
#include "rogueviz-pentagonal.cpp"
#include "rogueviz-graph.cpp"

View File

@ -151,68 +151,49 @@ struct indenter {
void doindent() { for(int i=0; i<current_indentation; i++) printf(" "); }
struct exp_parser {
string s;
int at;
exp_parser() { at = 0; }
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 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(next() == '(') at++, res = parsepar();
else {
string number;
while(true) {
char c = next();
if((c >= '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;