From 9f6d2a5d89ebf7dfd578527fabac31c0001ff708 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Fri, 12 Jul 2019 23:25:17 +0200 Subject: [PATCH] rogueviz:: newconformist explorable explanation added --- hyperweb.cpp | 8 + rogueviz-newconf.cpp | 766 +++++++++++++++++++++++++++++++++++++++++++ rogueviz.cpp | 1 + 3 files changed, 775 insertions(+) create mode 100644 rogueviz-newconf.cpp diff --git a/hyperweb.cpp b/hyperweb.cpp index 5f6faa94..93d92f27 100644 --- a/hyperweb.cpp +++ b/hyperweb.cpp @@ -17,6 +17,14 @@ #define MAXMDIM 3 #define CAP_COMPLEX2 0 +// we want newconformist, but we don't want CAP_GD there +#define CAP_NCONF 1 +#define CAP_GD 0 + +#ifndef EMSCRIPTEN +#define EMSCRIPTEN +#endif + #ifndef CAP_ORIENTATION #define CAP_ORIENTATION 1 #endif diff --git a/rogueviz-newconf.cpp b/rogueviz-newconf.cpp new file mode 100644 index 00000000..67adcd20 --- /dev/null +++ b/rogueviz-newconf.cpp @@ -0,0 +1,766 @@ +// newconformist explorable explanation +// example commandline: -noplayer -rugtsize 4096 -smart 1 -canvas B -ncee +// set CAP_NCONF (and change the path) if you have access to newconformist + +#ifndef CAP_NCONF +#define CAP_NCONF 0 +#endif + +#if CAP_NCONF +#ifndef CAP_DRAW +#define CAP_DRAW 0 +#endif +#define main nconf_main +#undef unordered_map +#include "nconf.cpp" +#undef main +#endif + +namespace hr { + +namespace nconf2 { + +enum class ptype : char { outside, inside, inside_left_up, inside_left_down, top, bottom, left_inf, right_inf, marked }; + +void add_border(vector& v, int cy) { + int Y = isize(v), X = isize(v[0]); + char nx = '6'; + for(int y=0; y cy ? '5' : (nx++); + } + +vector gensquare(int X, int Y) { + vector res(Y+4, string (X+4, '0')); + for(int y=0; y gent(int X, int Y) { + vector res(Y+X+4, string (Y+X+X+4, '0')); + for(int y=0; y fmap = gensquare(13, 13); + +vector snake = { + "00000000000000000000000000000000000", + "00444444444444444444444444444444400", + "04111111111111111111111111111111140", + "04111111111111111111111111111111140", + "06111111111111111111111111111111140", + "05111111111111111111111111111111140", + "05111111111111111111111111111111140", + "00555555555555555555555555511111140", + "00000000000000000000000000051111140", + "00555555555555555555555555511111140", + "05111111111111111111111111111111140", + "05111111111111111111111111111111140", + "05111111111111111111111111111111140", + "05111111111111111111111111111111140", + "05111111111111111111111111111111140", + "05111111444444444444444444444444400", + "05111114000000000000000000000000000", + "05111111444444444444444444444444400", + "05111111111111111111111111111111140", + "05111111111111111111111111111111140", + "05111111111111111111111111111111170", + "05111111111111111111111111111111150", + "05111111111111111111111111111111150", + "00555555555555555555555555555555500", + "00000000000000000000000000000000000" + }; + +struct coord { + int x, y; + coord operator + (int d) { + coord res = *this; + d &= 3; + if(d == 0) res.x++; + if(d == 1) res.y++; + if(d == 2) res.x--; + if(d == 3) res.y--; + return res; + } + }; + +char out = '-'; + +char& fmap_at(coord c) { return c.x >= 0 && c.x < isize(fmap[0]) && c.y >= 0 && c.y < isize(fmap) ? fmap[c.y][c.x] : out; }; + +ld vx[256][256], vy[256][256]; + +void reset_vxy() { for(int y=0; y<256; y++) for(int x=0; x<256; x++) vy[y][x] = vx[y][x] = 0; } + +ld cscale; + +bool pretty = true; + +void iterate() { + int Y = isize(fmap); + int X = isize(fmap[0]); + for(int y=0; y '0' : among(c, '1', '6', '7'); }; + if(y > 0 && in(fmap[y-1][x])) qty++, total += vx[y-1][x]; + if(y < Y-1 && in(fmap[y+1][x])) qty++, total += vx[y+1][x]; + if(x > 0 && in(fmap[y][x-1])) qty++, total += vx[y][x-1]; + if(x < X-1 && in(fmap[y][x+1])) qty++, total += vx[y][x+1]; + vx[y][x] = total / qty; + } + } + + vector xes; + + for(int y=0; y vs; + +bool viewmap = true; + +void fix_border() { + int Y = isize(fmap); + int X = isize(fmap[0]); + for(string& s: fmap) for(char& c: s) if(c == '5') c = '4'; + + coord cc; + for(int y=0; y=0; y--) fmap[y] = fmap[y/2]; + + int Y = isize(fmap); + int X = isize(fmap[0]); + + for(int y=Y-1; y>=0; y--) for(int x=X-1; x>=0; x--) + vx[y][x] = vx[y/2][x/2], vy[y][x] = vy[y/2][x/2]; + + for(int y=0; y= '4') { + bool live = false; + for(int k=0; k<4; k++) if(fmap_at(cc+k) == '1') live = true; + if(!live) us = '0'; + } + } + + bool found6 = false, found7 = false; + for(int y=0; y= X || y >= Y) return; + if(pointmode) { + if(fmap[y][x] < '4') return; + for(string& s: fmap) for(char& c: s) if(c == pointmode) c = '4'; + fmap[y][x] = pointmode; + fix_border(); + return; + } + if(y == 0) { + paintmode = 0; + fmap.emplace_back(); + for(int i=Y-1; i>=0; i--) fmap[i+1] = fmap[i]; + for(char& c: fmap[0]) c = '0'; + } + if(y == Y-1) { + paintmode = 0; + fmap.push_back(fmap[0]); + for(char& c: fmap.back()) c = '0'; + } + if(x == 0) { + paintmode = 0; + for(string& s: fmap) s = '0' + s; + } + if(x == X-1) { + paintmode = 0; + for(string& s: fmap) s = s + '0'; + } + coord cc{x,y}; + auto& us = fmap[y][x]; + if(fmap[y][x] >= '4') { + if(fmap[y][x] > '5') { + int q = 0; + for(int k=0; k<4; k++) if(fmap_at(cc+k) == '0') q++; + if(q != 1) return; + } + else { + for(int k=0; k<4; k++) if(fmap_at(cc+k) == (us^ '1')) return; + int q = 0; + for(int k=0; k<4; k++) if(fmap_at(cc+k) >= '4') q++; + if(q > 2) return; + } + for(int k=0; k<4; k++) + if(fmap_at(cc+k) != '1') + if(fmap_at(cc+k+(k+1)) == '1') + if(fmap_at(cc+(k+1)) != '1') + return; + + for(int k=0; k<4; k++) + if(fmap_at(cc+k) != '1') + if(fmap_at(cc+k+k) == '1') + if(fmap_at(cc+k+(k+1)) != '1') + if(fmap_at(cc+k+(k-1)) != '1') + return; + + if(fmap[y-1][x] == '0') fmap[y-1][x] = us; + if(fmap[y][x-1] == '0') fmap[y][x-1] = us; + if(fmap[y+1][x] == '0') fmap[y+1][x] = us; + if(fmap[y][x+1] == '0') fmap[y][x+1] = us; + us = '1'; + } + else if(us == '1') { + int q = 0; + for(int k=0; k<4; k++) if(fmap_at(cc+k) == '1') q++; + if(q == 0) return; + if(q == 4) return; + if(q == 3) { + for(int k=0; k<4; k++) if(fmap_at(cc+k) != '1') { + int nei = 0; + if(fmap_at(cc+k+(k+1)) == '1') nei++; + if(fmap_at(cc+k+(k+3)) == '1') nei++; + if(nei == 2) return; + us = fmap_at(cc+k); + if(nei == 0) fmap_at(cc+k) = '0'; + } + return; + } + if(q == 2 && fmap_at(cc+0) == '1' && fmap_at(cc+2) == '1') return; + if(q == 2 && fmap_at(cc+1) == '1' && fmap_at(cc+3) == '1') return; + for(int k=0; k<4; k++) if(fmap_at(cc+k) == '1' && fmap_at(cc+(k+1)) == '1' && fmap_at(cc+k+(k+1)) != '1') return; + bool have4 = false, kill6 = false, kill7 = false, live[4]; + for(int k=0; k<4; k++) { + char ch = fmap_at(cc+k); + + live[k] = false; + for(int k=0; k<4; k++) { + for(int l=0; l<4; l++) if(k!=(l^2) && fmap_at(cc+k+l) == '1') live[k] = true; + } + + switch(ch) { + case '4': have4 = true; break; + case '5': break; + case '6': if(!live[k]) kill6 = true; break; + case '7': if(!live[k]) kill7 = true; break; + }; + } + + if(kill6 && kill7) return; + + if(kill6) us = '6'; + else if(kill7) us = '7'; + else if(have4) us = '4'; + else us = '5'; + + for(int k=0; k<4; k++) if(!live[k]) fmap_at(cc+k) = '0'; + } + }; + +bool showmenu = true; + +void conf_shapes() { + cmode = 0; + dialog::init(XLAT("shapes")); + dialog::addItem("square 11x11", 'a'); + dialog::add_action([] { fmap = gensquare(13, 13); reset_vxy(); popScreen(); }); + dialog::addItem("rectangle 15x8", 'b'); + dialog::add_action([] { fmap = gensquare(15, 8); reset_vxy(); popScreen(); }); + dialog::addItem("T-shape", 'c'); + dialog::add_action([] { fmap = gent(19, 7); reset_vxy(); popScreen(); }); + dialog::addItem("snake", 'd'); + dialog::add_action([] { fmap = snake; reset_vxy(); popScreen(); }); + dialog::addBreak(100); + dialog::addBack(); + dialog::display(); + } + +int algo_speed = 10000; + +int algo_ticks = 0; + +bool in_visualization; +int nconf_pos; +ld steps_to_do; + +#define DELTA [] { static int oldticks = ticks; int res = ticks - oldticks; oldticks = ticks; return res; } + +auto nconf_delta = DELTA; + +#if CAP_NCONF +void nconf_prepare(bool fast) { + // pretend we are solving + nconf_solve(); + build_equations(nconf::pts, 1, fast); + nconf_pos = 0; + in_visualization = true; + steps_to_do = 0; + algo_ticks = 0; + nconf_delta(); + printf("points: %d\n", isize(nconf::allpoints)); + } + +void nconf_run() { + int d = nconf_delta(); + algo_ticks += d; + steps_to_do += d * algo_speed / 1000.; + while(steps_to_do > 0) { + if(nconf_pos == isize(nconf::allpoints)) { in_visualization = false; break; } + auto co = nconf::allpoints[nconf_pos++]; + auto &p = nconf::pts[co]; + nconf::eliminate(nconf::pts, co); + printf("point #%d has %d equations\n", nconf_pos-1, hr::isize(p.eqs)); + steps_to_do -= pow(hr::isize(p.eqs), 2); + } + } + +void pick_algorithm() { + cmode = 0; + dialog::init(XLAT("solving")); + dialog::addItem("iterative/reset", 'r'); + dialog::add_action([] { reset_vxy(); algo_ticks = 0; popScreen(); }); + dialog::addItem("solve linear equations", 's'); + dialog::add_action([] { nconf_solve(); algo_ticks = 0; popScreen(); }); + dialog::addItem("visualize (slow)", 'a'); + dialog::add_action([] { nconf_prepare(false); popScreen(); }); + dialog::addItem("visualize (fast)", 'b'); + dialog::add_action([] { nconf_prepare(true); popScreen(); }); + dialog::addSelItem("visualization speed", its(algo_speed), 'v'); + dialog::add_action([] { dialog::editNumber(algo_speed, 100, 1000000, 0.1, 10000, "", ""), dialog::scaleLog(), dialog::dialogflags = 0, dialog::numberdark = DONT_SHOW; }); + dialog::addBreak(50); + dialog::addBoolItem_action("pretty corners", pretty, 'p'); + dialog::addBreak(50); + dialog::addBack(); + dialog::display(); + } +#endif + +namespace ncee_scr { + int X, Y, xc, yc, x0, y0, siz; + } + +void draw_ncee() { + using namespace ncee_scr; + auto cd = current_display; + + Y = isize(fmap); + X = isize(fmap[0]); + siz = min((cd->ysize / (show_mapping ? 2 : 1) - 5) / Y, showmenu ? (cd->xcenter -5 )*2/X : (cd->xsize - 5) / X); + + xc = 0; + yc = vid.yres * (show_mapping ? mapping_split : 1) / 2 - cd->ycenter; + + x0 = - int(siz * X / 2); + y0 = - int(siz * Y / 2); + + const ld period = 2.898149445355172 / M_PI * 2; + + dynamicval pm(pmodel, mdUnchanged); + dynamicval pg(geometry, gEuclid); + + initquickqueue(); + nctinf2.texture_id = rug::glbuf->renderedTexture; + nctinf2.tvertices.clear(); + + ld map_ypos = vid.yres * (mapping_split + 1) / 2 - cd->ycenter; + ld sca2 = (vid.yres * (1-mapping_split) / 2 - 10) / vid.scale; + + if(show_mapping) { + for(int iter=-10; iter<=10; iter++) { + ld maxx = period * vid.scale / 4; + ld scax = sca2 * maxx / 0.5; + ld xpos = scax * 2 * iter; + curvepoint(hpxy(xpos-scax, map_ypos-sca2)); + nctinf2.tvertices.push_back(glhr::makevertex(0.5-maxx, 0, 0)); + curvepoint(hpxy(xpos-scax, map_ypos+sca2)); + nctinf2.tvertices.push_back(glhr::makevertex(0.5-maxx, 1, 0)); + curvepoint(hpxy(xpos+scax, map_ypos-sca2)); + nctinf2.tvertices.push_back(glhr::makevertex(0.5+maxx, 0, 0)); + curvepoint(hpxy(xpos-scax, map_ypos+sca2)); + nctinf2.tvertices.push_back(glhr::makevertex(0.5-maxx, 1, 0)); + curvepoint(hpxy(xpos+scax, map_ypos-sca2)); + nctinf2.tvertices.push_back(glhr::makevertex(0.5+maxx, 0, 0)); + curvepoint(hpxy(xpos+scax, map_ypos+sca2)); + nctinf2.tvertices.push_back(glhr::makevertex(0.5+maxx, 1, 0)); + } + auto& q = queuecurve(0, show_mgrid ? 0x404040FF : 0xFFFFFFFF, PPR::LINE); + q.tinf = &nctinf2; + q.flags |= POLY_TRIANGLES; + q.offset_texture = 0; + } + + auto h = [&] (int x, int y) { return hpxy(x0 + x * siz + xc, y0 + y * siz + yc); }; + auto hc = [&] (int x, int y) { return hpxy(x0 + x * siz + siz/2 + xc, y0 + y * siz + siz/2 + yc); }; + + color_t typecols[8] = { + 0x101010FF, 0xD0D0D0FF, 0, 0, 0xF04040FF, 0x4040F0FF, 0xF0F040FF, 0x40F0F0FF + }; + + for(int x=0; xrenderedTexture; + nctinf.tvertices.clear(); + + static int z = 0; + + auto tri = [&] (const array& c) { + + int id = -1; + for(int i=0; i<3; i++) if(fmap_at(c[i])) id = i; + if(id == -1) return; + ld delta = (int((vx[c[id].y][c[id].x]) / cscale / period + 1000.5) - 1000) * period; + z = !z; + for(int s=0; s<3; s++) { + curvepoint(hc(c[s].x, c[s].y)); + nctinf.tvertices.push_back(glhr::makevertex((vx[c[s].y][c[s].x]/cscale-delta)*vid.scale/2+.5, vy[c[s].y][c[s].x]*vid.scale/2+.5, 0)); + } + }; + + if(viewmap && !in_visualization) for(int x=0; x '0' && fmap[y+1][x] > '0') { + if(fmap[y][x] > '0') tri(make_array(coord{x,y}, coord{x+1,y}, coord{x,y+1})); + if(fmap[y+1][x+1] > '0') tri(make_array(coord{x+1,y+1}, coord{x+1,y}, coord{x,y+1})); + } + else if(fmap[y][x] > '0' && fmap[y+1][x+1] > '0') { + if(fmap[y][x+1] > '0') tri(make_array(coord{x,y}, coord{x+1,y}, coord{x+1,y+1})); + if(fmap[y+1][x] > '0') tri(make_array(coord{x,y}, coord{x,y+1}, coord{x+1,y+1})); + } + } + + auto& q = queuecurve(0, (show_mgrid && show_mapping) ? 0x404040FF : 0xFFFFFFFF, PPR::LINE); + q.tinf = &nctinf; + q.flags |= POLY_TRIANGLES; + q.offset_texture = 0; + + hyperpoint vmap[256][256]; + + pair mpt = {(mousex - xc - cd->xcenter - x0) / siz, (mousey - yc - cd->ycenter - y0) / siz}; + + const color_t gridcol = 0xFFFFFFFF; + if(show_mapping && show_mgrid && !in_visualization) { + for(int x=0; x '0') + vmap[y][x] = hpxy(vx[y][x]/cscale * sca2 / 2, vy[y][x] * sca2 / 2+ map_ypos); + for(int x=0; x '0' && fmap[y+1][x] > '0') { + color_t col = (pair(x,y) == mpt || pair(x,y+1) == mpt) ? 0xFFFF00FF : gridcol; + dynamicval lw(vid.linewidth, vid.linewidth * (col == 0xFFFF00FF ? 4 : 1)); + queueline(hc(x, y), hc(x, y+1), col, 0, PPR::CIRCLE); + queueline(vmap[y][x], vmap[y+1][x], col, 0, PPR::CIRCLE); + } + if(x < X-2 && fmap[y][x] > '0' && fmap[y][x+1] > '0') { + color_t col = (pair(x,y) == mpt || pair(x+1,y) == mpt) ? 0xFFFF00FF : gridcol; + dynamicval lw(vid.linewidth, vid.linewidth * (col == 0xFFFF00FF ? 4 : 1)); + queueline(hc(x, y), hc(x+1, y), col, 0, PPR::CIRCLE); + queueline(vmap[y][x], vmap[y][x+1], col, 0, PPR::CIRCLE); + } + } + } + + for(int x=0; x<=X; x++) queueline(h(x,0), h(x,Y), 0x80808080); + for(int y=0; y<=Y; y++) queueline(h(0,y), h(X,y), 0x80808080); + quickqueue(); + + } + +int ncee_map_prepared; + +void prepare_ncee_map() { + ncee_map_prepared = 5; + pmodel = mdBand; + dynamicval cgl(vid.cells_generated_limit, 9999999); + dynamicval r(rug::display_warning, false); + // vid.consider_shader_projection = false; + vid.scale = 0.5; + rug::init(); + rug::prepareTexture(); + rug::rugged = false; + } + +void ncee() { + cmode = showmenu ? (sm::SIDE | sm::MAYDARK | sm::DIALOG_STRICT_X) : 0; + calcparam(); + + if(ncee_map_prepared < 5) { cmode = sm::NORMAL; ncee_map_prepared++; if(ncee_map_prepared == 5) prepare_ncee_map(); gamescreen(2); return; } + + if(in_visualization) + nconf_run(); + else + iterate(); + + draw_ncee(); + using namespace ncee_scr; + auto cd = current_display; + + getcstat = '-'; + + if(paintmode && mousepressed) { + int x = (mousex - cd->xcenter - xc - x0) / siz; + int y = (mousey - cd->ycenter - yc - y0) / siz; + if(fmap[y][x] == paintmode) + changepoint(x, y, false); + } + + if(showmenu) { + dialog::init(XLAT("conformal maps")); + dialog::addBoolItem("edit shape", pointmode == 0, 'e'); + dialog::addBoolItem("set left end", pointmode == '6', 'a'); + dialog::addBoolItem("set right end", pointmode == '7', 'b'); + dialog::addBreak(50); + dialog::addBoolItem("show the end result", viewmap, 'm'); + dialog::addBoolItem("display the band model", show_mapping, 's'); + if(show_mapping) + dialog::addBoolItem("display the grid on model", show_mgrid, 'g'); + else + dialog::addBreak(100); + dialog::addBreak(50); + if(show_mapping) + dialog::addSelItem("mapping split", fts(mapping_split), 'y'); + else + dialog::addBreak(100); + dialog::addSelItem("double precision", its(isize(fmap[0])) + "x" + its(isize(fmap)), 'd'); + #if CAP_NCONF + dialog::addItem("solving method", 'l'); + #endif + dialog::addItem("shapes", 't'); + dialog::addItem("hide the menu", 'v'); + dialog::addItem("stop", 'x'); + dialog::display(); + } + else + displayButton(vid.xres - 8, 8 + vid.fsize, XLAT("(v) menu"), 'v', 16); + + if(algo_ticks) + displaystr(8, 8 + vid.fsize, 0, vid.fsize * 2, format("%d.%03d", algo_ticks/1000, algo_ticks%1000), 0xFFFFFF, 0); + + keyhandler = [=] (int sym, int uni) { + // dialog::handleNavigation(sym, uni); + if(uni == 'z') + prepare_ncee_map(); + + if(uni == 'x') { + popScreen(); + // rug::rugged = true; + rug::close(); + } + if(uni == 'e') pointmode = 0; + if(uni == 'a') pointmode = '6'; + if(uni == 'b') pointmode = '7'; + #if CAP_NCONF + if(uni == 'l') pushScreen(pick_algorithm); + #endif + // if(uni == 'w') edit_whatever('f', 0); + if(uni == 'd') doublemap(); + if(uni == 'm') viewmap = !viewmap; + if(uni == 's') show_mapping = !show_mapping; + if(uni == 'g') show_mgrid = !show_mgrid; + if(uni == 't') pushScreen(conf_shapes); + if(uni == 'y') dialog::editNumber(mapping_split, 0, 1, 0.05, 0.75, "", ""), dialog::dialogflags = 0, dialog::numberdark = DONT_SHOW; + if(uni == '-') { + int x = (mousex - cd->xcenter - xc - x0) / siz; + int y = (mousey - cd->ycenter - yc - y0) / siz; + if(x < 0 || y < 0 || x >= X || y >= Y) return; + paintmode = fmap[y][x]; + changepoint(x, y, true); + } + if(uni == 'v') showmenu = !showmenu; + }; + } + +#if ISWEB +extern "C" { + void nconf_view(int i) { + if(i == 1) + show_mapping = false, viewmap = false; + else if(i == 2) + show_mapping = false, viewmap = true; + else if(i == 3) + show_mapping = true, viewmap = true, show_mgrid = true; + else if(i == 4) + showmenu = false; + else if(i == 5) + showmenu = true; + else if(i == 6) + reset_vxy(), algo_ticks = 0; + #if CAP_NCONF + else if(i == 7) + nconf_solve(), algo_ticks = 0; + else if(i == 8) + nconf_prepare(false); + else if(i == 9) + nconf_prepare(true); + #endif + else if(i == 10) + doublemap(); + else if(i == 11) + fmap = gensquare(13, 13), reset_vxy(); + else if(i == 12) + fmap = gensquare(15, 8), reset_vxy(); + else if(i == 13) + fmap = gensquare(19, 7), reset_vxy(); + else if(i == 14) + fmap = snake, reset_vxy(); + } + } +#endif + +int niceArgs() { + using namespace arg; + + if(0) ; + else if(argis("-ncee")) { + PHASE(3); + pushScreen(ncee); + showstartmenu = false; + clearMessages(); + } + else if(argis("-ncv")) { + PHASE(3); + shift(); + nconf_view(argi()); + } + else return 1; + return 0; + } + +auto nhook = + addHook(hooks_args, 100, niceArgs) ++ 0; + +} + +} diff --git a/rogueviz.cpp b/rogueviz.cpp index 3fcc2ada..4a3a8c77 100644 --- a/rogueviz.cpp +++ b/rogueviz.cpp @@ -2479,3 +2479,4 @@ auto hooks = #include "rogueviz-flocking.cpp" #include "rogueviz-magiccube.cpp" #include "rogueviz-cvl.cpp" +#include "rogueviz-newconf.cpp"