renamed the RogueViz files

This commit is contained in:
Zeno Rogue
2019-09-12 22:50:16 +02:00
parent 30311e3929
commit cbcc0f28ed
14 changed files with 13 additions and 15 deletions
+525
View File
@@ -0,0 +1,525 @@
// Banach-Tarski animation in RogueViz.
// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
// good parameters: -fixx 10 -W Cros -bantar_anim
// use -bantar_map to see how it works
namespace rogueviz { namespace banachtarski {
bool on;
typedef vector<int> cwpath;
cwpath invertpath(cwpath p) {
cwpath res;
for(int i=0; i<isize(p); i++)
res.push_back(-p[isize(p)-1-i]);
return res;
}
array<cwpath, 4> gens;
cellwalker bttargets[4];
cellwalker trace(cellwalker cw, cwpath& p) {
for(int i: p) if(i == 0) cw += wstep; else cw += i;
return cw;
}
set<cell*> testlist;
map<cell*, cell*> parent;
bool test_uniq(cellwalker cw, int z, int lev, cell *par) {
if(testlist.count(cw.at)) return false;
testlist.insert(cw.at);
if(par) parent[cw.at] = par;
if(celldist(cw.at) > 9) return true;
/* if(cw.at->wall == waSea) {
printf("test_uniq failed\n");
cw.at->wall = waEternalFire;
}
cw.at->wall = waSea; */
if(lev) for(int y=0; y<4; y++) if(y != z)
if(!test_uniq(trace(cw, gens[y]), y^2, lev-1, cw.at))
return false;
return true;
}
template<class T> void recursively(cell *c, cell *c1, const T& t) {
t(c);
for(int i=0; i<c->type; i++)
if(c->move(i) && c->c.spin(i) == 0 && c->move(i) != c1)
recursively(c->move(i), c, t);
}
vector<cell*> allcells;
set<cell*> seen;
struct cellinfo {
cell *c;
int gid;
int spdist;
eWall w;
eItem it;
eLand land;
eMonster mo;
vector<int> way;
cwpath pinv;
};
map<cell*, cellinfo> infos;
int cidd = 0;
int more = 5;
void debugpath(vector<int>& way, cwpath& pinv) {
printf("pinv:"); for(int i: pinv) printf(" %d", i); printf("\n");
cellwalker cw = cwt;
cellwalker last_cw = cw;
int lastd = 0;
printf("way:"); for(int i: way) {
cellwalker cw2 = trace(cw, pinv);
printf(" [%d]", lastd = celldist(cw2.at));
printf(" %d", i);
last_cw = cw;
cw = trace(cw, gens[i]);
}
cellwalker cw2 = trace(cw, pinv);
int curd;
printf(" [%d]", curd = celldist(cw2.at));
printf("\n");
if(lastd == 10 && curd == 2) {
int b = way.back();
way.pop_back();
printf("CW:"); for(int w: way) for(int wp: gens[w]) printf(" %d", wp);
printf(" {");
for(int wp: gens[b]) printf(" %d", wp);
printf(" }");
for(int i: pinv) printf(" %d", i);
way.push_back(b);
printf("\n");
}
}
void recursive_paint(cwpath& pinv, vector<int>& way, int noway) {
cellwalker cw = cwt;
for(int i: way) cw = trace(cw, gens[i]);
cw = trace(cw, pinv);
cell *c = cw.at;
/* if(cidd == 1 && way == vector<int>{1,2,3})
c->item = itPirate; */
if(seen.count(c)) {
printf("seen error [%d]\n", celldist(c));
debugpath(way, pinv);
debugpath(infos[c].way, infos[c].pinv);
return;
}
seen.insert(c);
int hsh = 7;
for(int w: way) hsh = 11301 * hsh + w * 37121;
bool all0 = true;
for(int w: way) if(w) all0 = false;
int gid;
/* if(d)
c->landparam = 0x202020;
else */
if(all0 || way[0] == 2)
gid = 0;
else if(way[0] == 0)
gid = 1;
else if(way[0] == 1)
gid = 2;
else if(way[0] == 3)
gid = 3;
else
gid = 3;
infos[c] = cellinfo{c, gid, 0};
infos[c].way = way;
infos[c].pinv = pinv;
// c->landparam ^= ((isize(way)&1) * 0x3F3F3F);
// c->landparam = hsh; // d * 5 + 256 * (hsh&0xFFFF) + 0x400000;
if(cidd>112899) c->landparam = 0x101010;
// c->landparam = cidd * 0x1241C3;
if(celldist(c) <= 11+more) for(int i=0; i<4; i++) if(i != noway) {
vector<int> newway = {i};
for(int ii: way) newway.push_back(ii);
recursive_paint(pinv, newway, i^2);
}
}
bool once = true;
cwpath path_to(cell *c, int dir = 0) {
cwpath p;
cellwalker cw(c, dir);
while(cw != cwt) {
if(celldist((cw+wstep).at) < celldist(cw.at))
p.push_back(0), cw += wstep;
else {
if(p.size() && p.back()) p.back()++;
else p.push_back(1);
cw += 1;
}
}
return p;
}
void bantar_note(cell *c) {
if(seen.count(c)) return;
cwpath pinv = invertpath(path_to(c));
vector<int> way;
recursive_paint(pinv, way, 4);
cidd++;
}
using bantar_config = pair<cell*, cell*>;
tuple<ld,bool,ld> quality(bantar_config cp) {
hyperpoint h1 = tC0(ggmatrix(cp.first));
hyperpoint h2 = tC0(ggmatrix(cp.second));
return make_tuple(hdist0(h1) * hdist0(h2), h2[1] > 0, abs(h2[0] / h2[1]));
}
int notry = 0;
void bantar() {
if(!on) return;
cwt = cellwalker(currentmap->gamestart(), 0);
viewctr = heptspin(cwt.at->master, 0);
infos.clear();
vector<bantar_config> genchoices;
int lnotry = notry;
{
celllister clgen(cwt.at, 4, 1000000, NULL);
for(cell *c1: clgen.lst) if(c1->type == S7)
for(cell *c2: clgen.lst) if(c2->type == S7)
genchoices.emplace_back(c1, c2);
stable_sort(genchoices.begin(), genchoices.end(), [] (const bantar_config b1, const bantar_config b2) { return quality(b1) < quality(b2); });
for(bantar_config bc: genchoices) {
if(get<0>(quality(bc)) >= 4) exit(1);
for(int i=0; i<S7; i++)
for(int j=0; j<S7; j++) {
gens[0] = path_to(bc.first, i);
gens[1] = path_to(bc.second, j);
gens[2] = invertpath(gens[0]);
gens[3] = invertpath(gens[1]);
testlist.clear();
parent.clear();
bool tres = test_uniq(cwt, -1, 15, NULL);
auto q = quality(bc);
if(tres) {
DEBB(DF_LOG, ("gens = ", gens));
DEBB(DF_LOG, ("testing quality ", q, " ", make_pair(celldist(bc.first), celldist(bc.second)), ", result = ", tres));
lnotry--; if(lnotry <= 0) goto picked;
}
// if(tres) goto picked;
}
}
}
picked:
/*
if(S7 == 8) {
gens[1] = {0,4,0,4};
gens[0] = {2,0,2};
}
else {
gens[0] = {0,4,0,4};
gens[1] = {2,0,4,0,2};
}
gens[2] = invertpath(gens[0]);
gens[3] = invertpath(gens[1]);
*/
for(int i=0; i<4; i++) bttargets[i] = trace(cwt, gens[i]);
celllister cl(cwt.at, 8+more, 1000000, NULL);
// recursively(cwt.at, NULL, [] (cell *c) { allcells.push_back(c); } );
for(cell* c: cl.lst) bantar_note(c);
for(cell *c: cl.lst) if(infos.count(c) && infos[c].gid == 0)
forCellEx(c2, c) if(infos.count(c2) && infos[c2].gid != 0)
c->bardir = NOBARRIERS;
for(int it=0; it<ittypes; it++) if(itemclass(eItem(it)) == IC_TREASURE)
items[it] = 200;
}
vector<int> spdist;
int curpart;
/* bool hidebad(cell *c, const transmatrix& V) {
if(c->wparam != curpart && curpart < 4) return true;
return false;
} */
heptspin cth(cellwalker cw) {
return heptspin(cw.at->master, cw.spin, cw.mirrored);
}
ld alphaof(hyperpoint h) {
return atan2(h[1], h[0]);
}
#define ForInfos for(auto& cci: infos)
void bantar_frame() {
setGLProjection();
ForInfos
cci.second.w = cci.second.c->wall,
cci.second.it = cci.second.c->item,
cci.second.mo = cci.second.c->monst,
cci.second.land = cci.second.c->land,
cci.second.c->wparam = cci.second.gid;
calcparam();
vector<unique_ptr<drawqueueitem>> subscr[4];
compute_graphical_distance();
const int tmax = 2000;
int t = ticks % (5*tmax);
int tphase = t / tmax, tsub = t % tmax;
ld xdst, ydst;
for(int i=0; i<4; i++) {
ptds.clear();
cellwalker xcw;
ld part = 1;
if(i == 1) xcw = bttargets[0];
else if(i == 2) xcw = bttargets[1], part = .5;
else if(i == 3) xcw = bttargets[3], part = .5;
else xcw = cwt;
View = Id;
transmatrix tView = actualV(cth(xcw), Id) * calc_relative_matrix(cwt.at, xcw.at, NOHINT) * inverse(actualV(cth(cwt), Id));
if(tphase < 2) part = 0;
else if(tphase == 2)
part = part * tsub / tmax;
transmatrix itView = inverse(tView);
transmatrix z = rspintox(itView*C0) * xpush(hdist0(itView*C0) * part) * spintox(itView*C0);
transmatrix ful = rspintox(itView*C0) * xpush(hdist0(itView*C0) * tmax / tmax) * spintox(itView*C0); // rgpushxto0(itView*C0);
hyperpoint C1 = xpush0(1);
ld bof = alphaof(tView * ful * C1);
z = z * spin(bof * part);
View = inverse(z);
if(tphase == 0 && tsub > tmax/2) {
ld alpha = rand() % 10;
ld d = (rand() % 1000) / 20000. * (tsub-tmax/2) / (tmax/2);
View = spin(alpha) * xpush(d) * spin(-alpha);
}
/* int phasemask = 3;
if(tphase == 0) phasemask = 0;
if(tphase == 4) phasemask = 2; */
ForInfos if(cci.second.gid == i)
cci.second.c->wall = cci.second.w,
cci.second.c->item = cci.second.it,
cci.second.c->monst = cci.second.mo,
cci.second.c->land = cci.second.land;
else
cci.second.c->wall = waInvisibleFloor,
cci.second.c->item = itNone,
cci.second.c->monst = moNone,
cci.second.c->land = laNone;
mapeditor::drawplayer = cwt.at->wparam == i;
switch(tphase) {
case 0:
xdst = ydst = 0;
curpart = 4;
break;
case 1:
xdst = ydst = .5 * tsub / tmax;
break;
case 2:
xdst = ydst = .5;
break;
case 3:
xdst = .5, ydst = .5 * (tmax-tsub) / tmax;
break;
case 4:
xdst = .5, ydst = 0;
break;
default:
xdst = ydst = 0;
}
/* ld xpos = (!(i&2)) ? xdst : -xdst;
ld ypos = (!(i&1)) ? ydst : -ydst; */
gmatrix.clear();
currentmap->draw();
if(0) for(auto p: parent) if(gmatrix.count(p.first) && gmatrix.count(p.second) && infos[p.first].gid == i && infos[p.second].gid == i)
queueline(tC0(gmatrix[p.first]), tC0(gmatrix[p.second]), 0xFFFFFFFF, 2);
subscr[i] = move(ptds);
}
map<int, map<int, vector<unique_ptr<drawqueueitem>>>> xptds;
for(int i=0; i<4; i++) for(auto& p: subscr[i])
xptds[int(p->prio)][i].push_back(move(p));
for(auto& sm: xptds) for(auto& sm2: sm.second) {
int i = sm2.first;
ptds.clear();
for(auto& p: sm2.second) ptds.push_back(move(p));
vid.scale = .5;
vid.xposition = (!(i&2)) ? xdst : -xdst;
vid.yposition = (!(i&1)) ? ydst : -ydst;
calcparam();
drawqueue();
}
ForInfos
cci.second.c->wall = cci.second.w,
cci.second.c->item = cci.second.it,
cci.second.c->monst = cci.second.mo,
cci.second.c->land = cci.second.land;
}
void bantar_anim() {
vid.aurastr = 0;
bool breakanim = false;
int t = SDL_GetTicks();
drawthemap();
while(!breakanim) {
ticks = SDL_GetTicks() - t;
bantar_frame();
SDL_GL_SwapBuffers();
SDL_Event ev;
while(SDL_PollEvent(&ev))
if(ev.type == SDL_KEYDOWN || ev.type == SDL_MOUSEBUTTONDOWN)
breakanim = true;
}
mapeditor::drawplayer = true;
vid.xposition = vid.yposition = 0;
vid.scale = 1;
}
bool bmap;
void bantar_stats() {
if(bmap) {
vid.linewidth *= (inHighQual ? 10 : 2);
for(auto p: parent) if(gmatrix.count(p.first) && gmatrix.count(p.second))
queueline(tC0(gmatrix[p.first]), tC0(gmatrix[p.second]), 0x00FF00FF, 4);
double x = cgi.hexvdist;
for(auto gm: gmatrix) for(cell *c: {gm.first})
if(euclid || !pseudohept(c)) for(int t=0; t<c->type; t++) if(infos.count(c) && infos.count(c->move(t)) && c->move(t) && infos[c].gid != infos[c->move(t)].gid)
if(euclid ? c->move(t)<c : (((t^1)&1) || c->move(t) < c))
queueline(gm.second * ddspin(c,t,-M_PI/S6) * xpush(x) * C0,
gm.second * ddspin(c,t,+M_PI/S6) * xpush(x) * C0,
0xFF0000FF, 1);
vid.linewidth /= (inHighQual ? 10 : 2);
drawqueue();
}
}
void init_bantar() {
if(!on) {
stop_game();
on = true;
start_game();
}
}
void init_bantar_map() {
bmap = true;
ForInfos {
int hsh = 0x202047;
for(int w: cci.second.way) hsh = (11301 * hsh + w * 37121) & 0x7F7F7F;
cci.second.c->landparam = hsh;
cci.second.c->land = laCanvas;
cci.second.c->wall = waNone;
cci.second.c->item = itNone;
cci.second.c->monst = moNone;
}
}
int readArgs() {
using namespace arg;
if(0) ;
else if(argis("-bantar_anim")) {
PHASE(3);
init_bantar();
peace::on = true;
airmap.clear();
ForInfos if(cci.second.c->monst == moAirElemental)
cci.second.c->monst = moFireElemental;
bantar_anim();
}
else if(argis("-bantar_test")) {
PHASE(3);
init_bantar();
peace::on = true;
airmap.clear();
ForInfos if(cci.second.c->monst == moAirElemental)
cci.second.c->monst = moFireElemental;
ForInfos if(cci.second.gid != 2)
cci.second.c->wall = waInvisibleFloor,
cci.second.c->item = itNone,
cci.second.c->monst = moNone,
cci.second.c->land = laNone;
airmap.clear();
havewhat = 0;
}
else if(argis("-bantar_map")) {
init_bantar();
init_bantar_map();
}
else if(argis("-btry")) {
shift(); notry = argi();
}
else return 1;
return 0;
}
auto hook = addHook(hooks_args, 100, readArgs)
+ addHook(hooks_initgame, 100, bantar)
+ addHook(hooks_frame, 100, bantar_stats);
}}
+109
View File
@@ -0,0 +1,109 @@
// Copyright (C) 2018 Zeno and Tehora Rogue, see 'hyper.cpp' for details
// this is a plugin which generates branched tilings for newconformist
// https://github.com/zenorogue/newconformist (see the option '-cvl')
namespace hr {
#if CAP_SHOT
struct location {
transmatrix lView;
heptspin lviewctr;
};
struct lineinfo {
vector<location> locs;
int plus_matrices;
int minus_matrices;
};
map<int, lineinfo> lines;
location loc_multiply(location orig, transmatrix T) {
dynamicval<transmatrix> dv(View, orig.lView);
dynamicval<heptspin> dc(viewctr, orig.lviewctr);
View = inverse(T) * View;
for(int a=0; a<10; a++) optimizeview();
return location{View, viewctr};
}
bool show_map = false;
void cvl_marker() {
if(show_map) for(auto& l: lines) {
int id = 0;
for(auto& loc: l.second.locs) {
if(gmatrix.count(loc.lviewctr.at->c7)) {
transmatrix T = gmatrix[loc.lviewctr.at->c7] * inverse(spin(loc.lviewctr.spin*2*M_PI/S7 + master_to_c7_angle())) * inverse(loc.lView);
queuepoly(T, cgi.shAsymmetric, 0xFF00FFFF);
queuestr(T, 1.0, its(l.first)+"/"+its(id), 0xFFFFFF);
}
id++;
}
}
}
int readArgs() {
using namespace arg;
if(0) ;
else if(argis("-cvlbuild")) {
PHASEFROM(3);
start_game();
shift();
fhstream f(argcs(), "rt");
if(!f.f) { shift(); printf("failed to open file\n"); return 0; }
int id;
lineinfo l0;
scan(f, id, l0.plus_matrices, l0.minus_matrices);
l0.locs.push_back(location{View, viewctr});
for(int i=1; i<l0.plus_matrices; i++)
l0.locs.push_back(loc_multiply(l0.locs.back(), xpush(1)));
lines[id] = std::move(l0);
while(true) {
scan(f, id);
println(hlog, "id=", id, ".");
if(id < 0) break;
auto& l1 = lines[id];
int step;
scan(f, id, step);
transmatrix T;
for(int a=0; a<9; a++) scan(f, T[0][a]);
scan(f, l1.plus_matrices, l1.minus_matrices);
auto old = lines[id].locs[step];
println(hlog, "FROM ", old.lView, old.lviewctr, " id=", id, " step=", step);
l1.locs.push_back(loc_multiply(old, T));
println(hlog, "TO ", l1.locs.back().lView, l1.locs.back().lviewctr, "; creating ", l1.plus_matrices);
for(int i=1; i<l1.plus_matrices; i++)
l1.locs.push_back(loc_multiply(l1.locs.back(), xpush(1)));
println(hlog, "LAST ", l1.locs.back().lView, l1.locs.back().lviewctr);
}
}
else if(argis("-cvllist")) {
for(auto& l: lines)
for(auto& loc: l.second.locs) {
println(hlog, l.first, ". ", loc.lviewctr, " (dist=", celldist(loc.lviewctr.at->c7), "), View = ", loc.lView);
}
}
else if(argis("-cvlmap")) {
show_map = !show_map;
}
else if(argis("-cvldraw")) {
shift(); string s = args();
for(auto& p: lines) {
int i = 0;
for(auto& loc: p.second.locs) {
dynamicval<transmatrix> dv(View, loc.lView);
dynamicval<heptspin> dc(viewctr, loc.lviewctr);
shot::take(format(s.c_str(), p.first, i++));
}
}
}
else return 1;
return 0;
}
auto magichook = addHook(hooks_args, 100, readArgs) + addHook(hooks_frame, 100, cvl_marker);
#endif
}
+452
View File
@@ -0,0 +1,452 @@
// flocking simulations
// Copyright (C) 2018 Zeno and Tehora Rogue, see 'hyper.cpp' for details
// based on Flocking by Daniel Shiffman (which in turn implements Boids by Craig Reynold)
// https://processing.org/examples/flocking.html
// Our implementation simplifies some equations a bit.
// example parameters:
// flocking on a torus:
// -tpar 21,4 -geo 6 -flocking 10 -rvshape 3
// flocking on the Zebra quotient:
// -geo 4 -flocking 10 -rvshape 3 -zoom .9
// press 'o' when flocking active to change the parameters.
#ifndef NO_THREADS
#include <thread>
int threads = 1;
#endif
template<class T> auto parallelize(long long N, T action) -> decltype(action(0,0)) {
#ifdef NO_THREADS
return action(0,N);
#else
if(threads == 1) return action(0,N);
std::vector<std::thread> v;
typedef decltype(action(0,0)) Res;
std::vector<Res> results(threads);
for(int k=0; k<threads; k++)
v.emplace_back([&,k] () {
results[k] = action(N*k/threads, N*(k+1)/threads);
});
for(std::thread& t:v) t.join();
Res res = 0;
for(Res r: results) res += r;
return res;
#endif
}
namespace rogueviz {
namespace flocking {
int N;
bool draw_lines = false;
int follow = 0;
string follow_names[3] = {"nothing", "specific boid", "center of mass"};
map<cell*, map<cell*, transmatrix>> relmatrices;
ld ini_speed = .5;
ld max_speed = 1;
ld sep_factor = 1.5;
ld sep_range = .25;
ld align_factor = 1;
ld align_range = .5;
ld coh_factor = 1;
ld coh_range = 2.5;
ld check_range = 2.5;
char shape = 'b';
vector<tuple<hyperpoint, hyperpoint, color_t> > lines;
// parameters of each boid
// m->base: the cell it is currently on
// m->vel: velocity
// m->at: determines the position and speed:
// m->at * (0, 0, 1) is the current position (in Minkowski hyperboloid coordinates relative to m->base)
// m->at * (m->vel, 0, 0) is the current velocity vector (tangent to the Minkowski hyperboloid)
// m->pat: like m->at but relative to the screen
void init() {
if(!bounded) {
addMessage("Flocking simulation needs a bounded space.");
return;
}
stop_game();
rogueviz::init(); kind = kFlocking;
vdata.resize(N);
const auto v = currentmap->allcells();
printf("computing relmatrices...\n");
// relmatrices[c1][c2] is the matrix we have to multiply by to
// change from c1-relative coordinates to c2-relative coordinates
for(cell* c1: v) {
manual_celllister cl;
cl.add(c1);
for(int i=0; i<isize(cl.lst); i++) {
cell *c2 = cl.lst[i];
transmatrix T = calc_relative_matrix(c2, c1, C0);
if(hdist0(tC0(T)) <= check_range) {
relmatrices[c1][c2] = T;
forCellEx(c3, c2) cl.add(c3);
}
}
}
printf("setting up...\n");
for(int i=0; i<N; i++) {
vertexdata& vd = vdata[i];
// set initial base and at to random cell and random position there
createViz(i, v[hrand(isize(v))], spin(hrand(100)) * xpush(hrand(100) / 200.));
vd.name = its(i+1);
vd.cp = dftcolor;
vd.cp.color2 = ((hrand(0x1000000) << 8) + 0xFF) | 0x808080FF;
vd.cp.shade = shape;
vd.m->vel = ini_speed;
}
storeall();
printf("done\n");
}
int precision = 10;
void simulate(int delta) {
int iter = 0;
while(delta > precision && iter < 100) {
simulate(precision); delta -= precision;
iter++;
}
ld d = delta / 1000.;
int N = isize(vdata);
vector<transmatrix> pats(N);
vector<ld> vels(N);
using shmup::monster;
map<cell*, vector<monster*>> monsat;
for(int i=0; i<N; i++) {
vertexdata& vd = vdata[i];
auto m = vd.m;
monsat[m->base].push_back(m);
}
lines.clear();
parallelize(N, [&monsat, &d, &vels, &pats] (int a, int b) { for(int i=a; i<b; i++) {
vertexdata& vd = vdata[i];
auto m = vd.m;
transmatrix I = inverse(m->at);
// we do all the computations here in the frame of reference
// where m is at (0,0,1) and its velocity is (m->vel,0,0)
hyperpoint velvec = hpxyz(m->vel, 0, 0);
hyperpoint sep = hpxyz(0, 0, 0);
int sep_count = 0;
hyperpoint align = hpxyz(0, 0, 0);
int align_count = 0;
hyperpoint coh = hpxyz(0, 0, 0);
int coh_count = 0;
for(auto& p: relmatrices[m->base]) {
auto f = monsat.find(p.first);
if(f != monsat.end()) for(auto m2: f->second) if(m != m2) {
ld vel2 = m2->vel;
transmatrix at2 = I * p.second * m2->at;
// at2 is like m2->at but relative to m->at
// m2's position relative to m (tC0 means *(0,0,1))
hyperpoint ac = tC0(at2);
// distance and azimuth to m2
ld di = hdist0(ac);
transmatrix alphaspin = rspintox(ac); // spin(-atan2(ac));
color_t col = 0;
if(di < align_range) {
// we need to transfer m2's velocity vector to m's position
// this is done by applying an isometry which sends m2 to m1
// and maps the straight line on which m1 and m2 are to itself
align += gpushxto0(ac) * at2 * hpxyz(vel2, 0, 0);
align_count++;
col |= 0xFF0040;
}
if(di < coh_range) {
// azimuthal equidistant projection of ac
// (thus the cohesion force pushes us towards the
// average of azimuthal equidistant projections)
coh += alphaspin * hpxyz(di, 0, 0);
coh_count++;
col |= 0xFF40;
}
if(di < sep_range && di > 0) {
sep -= alphaspin * hpxyz(1 / di, 0, 0);
sep_count++;
col |= 0xFF000040;
}
if(col && draw_lines)
lines.emplace_back(m->pat * C0, m->pat * at2 * C0, col);
}
}
// a bit simpler rules than original
if(sep_count) velvec += sep * (d * sep_factor / sep_count);
if(align_count) velvec += align * (d * align_factor / align_count);
if(coh_count) velvec += coh * (d * coh_factor / coh_count);
// hypot2 is the length of a vector in R^2
vels[i] = hypot_d(2, velvec);
transmatrix alphaspin = rspintox(velvec); // spin(-atan2(velvec));
if(vels[i] > max_speed) {
velvec = velvec * (max_speed / vels[i]);
vels[i] = max_speed;
}
pats[i] = m->at * alphaspin * xpush(vels[i] * d);
fixmatrix(pats[i]);
} return 0; });
for(int i=0; i<N; i++) {
vertexdata& vd = vdata[i];
auto m = vd.m;
// these two functions compute new base and at, based on pats[i]
m->at = pats[i];
virtualRebase(m, true);
m->vel = vels[i];
}
shmup::fixStorage();
}
bool turn(int delta) {
if(!on) return false;
if(kind == kFlocking) simulate(delta), timetowait = 0;
if(follow) {
if(follow == 1) {
gmatrix.clear();
vdata[0].m->pat = View * calc_relative_matrix(vdata[0].m->base, viewctr.at->c7, C0) * vdata[0].m->at;
View = spin(90 * degree) * inverse(vdata[0].m->pat) * View;
if(GDIM == 3) {
View = hr::cspin(1, 2, 90 * degree) * View;
}
}
if(follow == 2) {
// we take the average in R^3 of all the boid positions of the Minkowski hyperboloid
// (in quotient spaces, the representants closest to the current view
// are taken), and normalize the result to project it back to the hyperboloid
// (the same method is commonly used on the sphere AFAIK)
hyperpoint h = Hypc;
for(int i=0; i<N; i++) if(gmatrix.count(vdata[i].m->base)) {
vdata[i].m->pat = gmatrix[vdata[i].m->base] * vdata[i].m->at;
h += tC0(vdata[i].m->pat);
}
h = normalize(h);
View = gpushxto0(h) * View;
}
optimizeview();
centerover.at = viewctr.at->c7;
compute_graphical_distance();
gmatrix.clear();
playermoved = false;
}
return false;
// shmup::pc[0]->rebase();
}
#if CAP_COMMANDLINE
int readArgs() {
using namespace arg;
// options before reading
if(0) ;
else if(argis("-flocking")) {
PHASEFROM(2);
shift(); N = argi();
init();
}
else if(argis("-cohf")) {
shift(); coh_factor = argf();
}
else if(argis("-alignf")) {
shift(); align_factor = argf();
}
else if(argis("-sepf")) {
shift(); sep_factor = argf();
}
else if(argis("-checkr")) {
shift(); check_range = argf();
}
else if(argis("-cohr")) {
shift(); coh_range = argf();
}
else if(argis("-alignr")) {
shift(); align_range = argf();
}
else if(argis("-sepr")) {
shift(); sep_range = argf();
}
else if(argis("-flockfollow")) {
shift(); follow = argi();
}
else if(argis("-flockprec")) {
shift(); precision = argi();
}
else if(argis("-flockshape")) {
shift(); shape = argcs()[0];
for(int i=0; i<N; i++)
vdata[i].cp.shade = shape;
}
#ifndef NO_THREADS
else if(argis("-threads")) {
shift(); threads = argi();
}
#endif
else return 1;
return 0;
}
void flock_marker() {
if(draw_lines)
for(auto p: lines) queueline(get<0>(p), get<1>(p), get<2>(p), 0);
}
void show() {
cmode = sm::SIDE | sm::MAYDARK;
gamescreen(0);
dialog::init(XLAT("flocking"), iinf[itPalace].color, 150, 0);
dialog::addSelItem("initial speed", fts(ini_speed), 'i');
dialog::add_action([]() {
dialog::editNumber(ini_speed, 0, 2, .1, .5, "", "");
});
dialog::addSelItem("max speed", fts(max_speed), 'm');
dialog::add_action([]() {
dialog::editNumber(max_speed, 0, 2, .1, .5, "", "");
});
dialog::addSelItem("separation factor", fts(sep_factor), 's');
dialog::add_action([]() {
dialog::editNumber(sep_factor, 0, 2, .1, 1.5, "", "");
});
string rangehelp = "Increasing this parameter may also require increasing the 'check range' parameter.";
dialog::addSelItem("separation range", fts(sep_range), 'S');
dialog::add_action([rangehelp]() {
dialog::editNumber(sep_range, 0, 2, .1, .5, "", rangehelp);
});
dialog::addSelItem("alignment factor", fts(align_factor), 'a');
dialog::add_action([]() {
dialog::editNumber(align_factor, 0, 2, .1, 1.5, "", "");
});
dialog::addSelItem("alignment range", fts(align_range), 'A');
dialog::add_action([rangehelp]() {
dialog::editNumber(align_range, 0, 2, .1, .5, "", rangehelp);
});
dialog::addSelItem("cohesion factor", fts(coh_factor), 'c');
dialog::add_action([]() {
dialog::editNumber(coh_factor, 0, 2, .1, 1.5, "", "");
});
dialog::addSelItem("cohesion range", fts(coh_range), 'C');
dialog::add_action([rangehelp]() {
dialog::editNumber(coh_range, 0, 2, .1, .5, "", rangehelp);
});
dialog::addSelItem("check range", fts(check_range), 't');
dialog::add_action([]() {
ld radius = 0;
for(cell *c: currentmap->allcells())
for(int i=0; i<c->degree(); i++) {
hyperpoint h = nearcorner(c, i);
radius = max(radius, hdist0(h));
}
dialog::editNumber(check_range, 0, 2, .1, .5, "",
"Value used in the algorithm: "
"only other boids in cells whose centers are at most 'check range' from the center of the current cell are considered. "
"Should be more than the other ranges by at least double the cell radius (in the current geometry, double the radius is " + fts(radius*2) + "); "
"but too large values slow the simulation down.\n\n"
"Restart the simulation to apply the changes to this parameter. In quotient spaces, the simulation may not work correctly when the same cell is in range check_range "
"in multiple directions."
);
});
dialog::addSelItem("number of boids", its(N), 'n');
dialog::add_action([]() {
dialog::editNumber(N, 0, 1000, 1, 20, "", "");
});
dialog::addSelItem("precision", its(precision), 'p');
dialog::add_action([]() {
dialog::editNumber(precision, 0, 1000, 1, 10, "", "smaller number = more precise simulation");
});
dialog::addSelItem("change geometry", XLAT(ginf[geometry].shortname), 'g');
hr::showquotients = true;
dialog::add_action(runGeometryExperiments);
dialog::addBoolItem_action("draw forces", draw_lines, 'l');
dialog::addSelItem("follow", follow_names[follow], 'f');
dialog::add_action([] () { follow++; follow %= 3; });
dialog::addBreak(100);
dialog::addItem("restart", 'r');
dialog::add_action(init);
dialog::addBack();
dialog::display();
}
named_functionality o_key() {
if(kind == kFlocking) return named_dialog("flocking", show);
return named_functionality();
}
auto hooks =
addHook(hooks_args, 100, readArgs) +
addHook(shmup::hooks_turn, 100, turn) +
addHook(hooks_frame, 100, flock_marker) +
addHook(hooks_o_key, 80, o_key) +
0;
#endif
}
}
+159
View File
@@ -0,0 +1,159 @@
namespace rogueviz {
namespace graph {
vector<string> formula;
bool graph_on;
color_t graphcolor;
hyperpoint err = hpxyz(500,0,0);
bool iserror(hyperpoint h) { return sqhypot_d(2, h) > 10000 || std::isnan(h[0]) || std::isnan(h[1]) || std::isnan(h[2]) || std::isinf(h[0]) || std::isinf(h[1]) || std::isinf(h[2]); }
hyperpoint xy_to_point(ld x, ld y) {
if(sphere && hypot(x, y) > 1)
return err;
return hpxy(x, y);
}
hyperpoint find_point(ld t) {
exp_parser ep;
auto &dict = ep.extra_params;
dict["t"] = t;
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++;
cld x = ep.parse();
if(!ep.ok()) return err;
dict[varname] = x;
}
if(!dict.count("y") && dict.count("r"))
return xspinpush0(real(dict["phi"]), real(dict["r"]));
if(dict.count("z") && dict.count("x"))
return hpxyz(real(dict["x"]), real(dict["y"]), real(dict["z"]));
if(dict.count("z")) {
return xy_to_point(real(dict["z"]), imag(dict["z"]));
}
return xy_to_point(real(dict["x"]), real(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 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) {
dialog::addItem(dialog::view_edited_string(), '1'+i);
}
else {
dialog::addItem(formula[i], editwhich == -1 ? '1'+i : 0);
dialog::add_action([i] () { editwhich = i; dialog::start_editing(formula[i]); });
}
}
dialog::addBack();
dialog::display();
keyhandler = [] (int sym, int uni) {
if(editwhich >= 0) {
if(dialog::handle_edit_string(sym, uni)) ;
else if(doexiton(sym, uni))
editwhich = -1;
}
else {
handlePanning(sym, uni);
dialog::handleNavigation(sym, uni);
// if(doexiton(sym, uni)) popScreen();
}
};
}
void 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();
}
}
#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);
}
}
+237
View File
@@ -0,0 +1,237 @@
// show the fundamental domain for quotient spaces
// Copyright (C) 2018 Zeno and Tehora Rogue, see 'hyper.cpp' for details
namespace hr {
namespace fundamental {
color_t color1, color2;
map<cell*, int> same;
map<cell*, transmatrix> gm;
bool is_connected(cellwalker cw) {
return same[cw.at] & (1<<cw.spin);
}
void be_connected(cellwalker cw) {
// transmatrix T = gm[cw.at];
same[cw.at] |= (1<<cw.spin);
cw += wstep;
same[cw.at] |= (1<<cw.spin);
/* printf("%s", display(T * C0));
printf(" %s\n", display(gm[cw.at] * C0)); */
// queueline(T * C0, gm[cw.at] * C0, 0xFF0000FF, 3);
}
int funmode = 0;
hyperpoint corner(cellwalker cw) {
transmatrix T = gm[cw.at];
if(funmode == 2) {
while(cw.at->type != S7) {
cw++;
T = T * calc_relative_matrix(cw.peek(), cw.at, cw.spin);
cw += wstep;
}
return T * C0;
}
return gm[cw.at] * get_corner_position(cw.at, cw.spin+(cw.mirrored?0:1), 3);
}
transmatrix rel(cellwalker cw) {
return calc_relative_matrix(cw.cpeek(), cw.at, cw.spin);
}
ld label_dist = .3;
transmatrix labelpos(hyperpoint h1, hyperpoint h2) {
hyperpoint h = mid(h1, h2);
transmatrix T = rgpushxto0(h);
hyperpoint hx = inverse(T) * h2;
ld alpha = atan2(-hx[1], hx[0]);
return T * xspinpush(alpha + M_PI/2, label_dist);
}
ld widthfactor = 5;
ld label_scale = 1;
void fundamental_marker() {
if(!funmode || !(quotient || euwrap || elliptic)) return;
same.clear();
gm.clear();
same[cwt.at] = 0;
gm[cwt.at] = ggmatrix(cwt.at);
vector<cell*> cells;
cells.push_back(cwt.at);
int tree_edges = 0;
int face_edges = 0;
for(int k=0; k<isize(cells); k++) {
cell *c = cells[k];
for(int i=0; i<c->type; i++) {
cellwalker cw(c, i);
cell *c2 = cw.cpeek();
if(gm.count(c2)) continue;
gm[c2] = gm[c] * rel(cw);
// queueline(gm[c2] * C0, gm[c2] * xspinpush0(ticks, 0.2), 0xFFFFFFFF, 3);
be_connected(cw);
tree_edges++;
cells.push_back(c2);
}
}
while(true) {
int f = face_edges;
for(int k=0; k<isize(cells); k++) {
cell *c = cells[k];
for(int i=0; i<c->type; i++) {
cellwalker cw(c, i);
if(is_connected(cw) && is_connected(cw+1) && !is_connected(cw+wstep-1)) {
face_edges++;
be_connected(cw+wstep-1);
}
}
}
if(f == face_edges) break;
}
cellwalker cw;
int corners = 0;
for(int k=0; k<isize(cells); k++) {
cell *c = cells[k];
for(int i=0; i<c->type; i++) {
cellwalker cw0(c, i);
if(!is_connected(cw0) && !is_connected(cw0+1) && !is_connected(cw0+wstep-1))
corners++, cw = cw0;
}
}
// printf("tree edges = %d, face edges = %d, corners = %d\n", tree_edges, face_edges, corners);
map<cellwalker, cellwalker> next_corner;
map<cellwalker, cellwalker> prev_corner;
for(int ci=0; ci<corners; ci++) {
cellwalker cw0 = cw;
while(true) {
cw++;
if(is_connected(cw)) {
cw += wstep;
cw++;
}
if(!is_connected(cw+1) && !is_connected(cw+wstep-1))
break;
}
next_corner[cw0] = cw;
prev_corner[cw] = cw0;
}
vector<transmatrix> nearm;
for(int ci=0; ci<corners; ci++) {
for(int u=0; u<1; u++) {
cellwalker cw1 = cw+u+wstep+(u-1);
/* printf("%p/%d %p/%d ", cw.at, cw.spin, cw1.at, cw1.spin);
printf("[%d %d %d] ", is_connected(cw), is_connected(cw+1), is_connected(cw+wstep-1));
printf("[%d %d %d] ", is_connected(cw1), is_connected(cw1+1), is_connected(cw1+wstep-1));
printf("%d %d;\n", !!next_corner.count(cw1), !!next_corner.count(cw1+wmirror-1)); */
transmatrix T_here = gm[cw.at] * rel(cw+u);
transmatrix T_there = gm[cw1.at];
nearm.push_back(T_here * inverse(T_there));
}
cw = next_corner[cw];
}
vid.linewidth *= widthfactor;
for(int ci=0; ci<corners; ci++) {
hyperpoint h = corner(cw);
cw = next_corner[cw];
hyperpoint h2 = corner(cw);
for(auto& T: nearm) queueline(T * h, T * h2, color1, 3);
}
for(int ci=0; ci<corners; ci++) {
hyperpoint h = corner(cw);
cw = next_corner[cw];
hyperpoint h2 = corner(cw);
queueline(h, h2, color2, 3);
}
if(0) for(int k=0; k<isize(cells); k++) {
cell *c = cells[k];
for(int i=0; i<c->type; i++) {
cellwalker cw0(c, i);
if(!is_connected(cw0)) continue;
int v = 0;
for(auto& n: nearm) {
queueline(n * gm[cw0.at] * xspinpush0(v, .05), n * gm[cw0.cpeek()] * xspinpush0(v, .05), 0xFF8000FF, 0);
v++;
}
queueline(gm[cw0.at] * C0, gm[cw0.cpeek()] * C0, 0xFF0000FF, 0);
}
}
set<cellwalker> visited;
int id = 0;
for(int ci=0; ci<corners; ci++) {
cellwalker cw1 = (cw+1+wstep);
bool mirrored = false;
if(!next_corner.count(cw1)) cw1 = cw1 + wmirror - 1, mirrored = true;
// visited.insert(next_corner[cw]);
// cellwalker cw2 = next_corner[cw];
if(next_corner[cw] < (mirrored ? next_corner[cw1] : cw1)) {
int mc = (mirrored ? color1 : color2) >> 8;
if(hdist(corner(cw), corner(next_corner[cw])) > 1e-3) {
queuestr(labelpos(corner(cw), corner(next_corner[cw])), label_scale/cgi.scalefactor, its(id), mc);
if(mirrored)
queuestr(labelpos(corner(cw1), corner(next_corner[cw1])), label_scale/cgi.scalefactor, its(id), mc);
else
queuestr(labelpos(corner(prev_corner[cw1]), corner(cw1)), label_scale/cgi.scalefactor, its(id), mc);
id++;
}
}
cw = next_corner[cw];
}
vid.linewidth /= widthfactor;
}
int readArgs() {
using namespace arg;
if(0) ;
else if(argis("-fundamental")) {
shift(); funmode = argi();
shift(); color1 = arghex();
shift(); color2 = arghex();
shift_arg_formula(widthfactor);
shift_arg_formula(label_scale);
shift_arg_formula(label_dist);
}
else return 1;
return 0;
}
auto fundamentalhook = addHook(hooks_args, 100, readArgs) + addHook(hooks_frame, 100, fundamental_marker);
}
}
+1545
View File
File diff suppressed because it is too large Load Diff
+176
View File
@@ -0,0 +1,176 @@
namespace hr {
#if CAP_CRYSTAL
void performMarkCommand(cell *c);
namespace crystal {
typedef array<int, MAXDIM> coord;
heptagon *get_heptagon_at(coord c);
coord get_coord(heptagon *h);
void set_crystal(int sides);
}
void curvepoint(const hyperpoint& H1);
dqi_poly& queuecurve(color_t linecol, color_t fillcol, PPR prio);
namespace magic {
bool on = false;
int back = 0x202020;
int magiccolors[14] = { 0xFFFFFF, 0xFFFF00, 0x0000FF, 0x00FF00, 0xFF0000, 0xFF8000, 0x800080, 0x808080, 0x00FFFF, 0x80FFFF, 0x4040C0, 0x40C040, 0xC04040, 0xC0A040 };
int dim() { return ginf[gCrystal].sides / 2; }
void build(crystal::coord co, int at) {
if(at < dim()) {
for(int z: {0,2,-2,4,-4}) co[at] = z, build(co, at+1);
return;
}
for(int i=0; i<S7; i++) crystal::get_heptagon_at(co)->cmove(i);
int twos = 0, index = 0;
for(int a=0; a<dim(); a++)
if(co[a] == 4) twos++, index = 2*a;
else if(co[a] == -4) twos++, index = 2*a+1;
auto c = crystal::get_heptagon_at(co)->c7;
setdist(c, 7, NULL);
if(twos == 0)
c->landparam = back;
else if(twos == 1)
c->landparam = magiccolors[index];
println(hlog, co, " twos = ", twos, " index = ", index, " set = ", format("%06X", c->landparam));
}
void magic(int sides) {
stop_game();
crystal::set_crystal(sides);
set_variation(eVariation::pure);
firstland = specialland = laCanvas;
patterns::whichCanvas = 'g';
patterns::canvasback = 0;
check_cgi();
start_game();
build(crystal::c0, 0);
on = true;
}
void curveline(hyperpoint a, hyperpoint b, int lev) {
if(lev>0) {
hyperpoint c = mid(a, b);
curveline(a, c, lev-1);
curveline(c, b, lev-1);
}
curvepoint(b);
}
bool magic_markers(cell *c, const transmatrix& V) {
if(!on) return false;
timerghost = false;
if(c->landparam == back) {
for(int i=0; i<S7; i++) {
cell *c2 = c->move(i);
if(c2->landparam != back) {
hyperpoint h1 = V * get_corner_position(c, i, 3/.9);
hyperpoint h2 = V * get_corner_position(c, i+1, 3/.9);
curvepoint(h1);
curveline(h1, h2, 3);
hyperpoint h3 = V * get_corner_position(c, i, 3/.7);
hyperpoint h4 = V * get_corner_position(c, i+1, 3/.7);
curvepoint(h4);
curveline(h4, h3, 3);
queuecurve(0xFF, (c2->landparam << 8) | 0xFF, PPR::LINE);
}
}
}
else c->wall = waInvisibleFloor;
return false;
}
int readArgs() {
using namespace arg;
if(0) ;
else if(argis("-magic")) {
PHASEFROM(2);
shift(); magic(argi());
}
else return 1;
return 0;
}
void twos_to_fours(vector<int>& zeros, crystal::coord co, int d) {
if(d == dim()) {
int facetable[5][5];
for(int x=0; x<5; x++)
for(int y=0; y<5; y++) {
co[zeros[0]] = 2*x-4;
co[zeros[1]] = 2*y-4;
facetable[y][x] = crystal::get_heptagon_at(co)->c7->landparam;
}
for(int x=0; x<5; x++)
for(int y=0; y<5; y++) {
co[zeros[0]] = 2*y-4;
co[zeros[1]] = 4-2*x;
crystal::get_heptagon_at(co)->c7->landparam = facetable[y][x];
}
}
else {
twos_to_fours(zeros, co, d+1);
if(co[d] == 2 || co[d] == -2) {
co[d] *= 2;
twos_to_fours(zeros, co, d+1);
}
}
}
bool magic_rotate(cell *c) {
if(!on) return false;
if(c->landparam != back) return false;
vector<int> zeros;
auto co = crystal::get_coord(c->master);
println(hlog, "co = ", co);
for(int i=0; i<dim(); i++) {
if(co[i] == 0) zeros.push_back(i);
else if(co[i] == 2 || co[i] == -2) ;
else return false;
}
println(hlog, "zeros = ", zeros);
if(isize(zeros) != 2) return false;
twos_to_fours(zeros, co, 0);
return true;
}
bool magic_rugkey(int sym, int uni) {
if((cmode & sm::NORMAL) && uni == 'p' && on) {
rug::texturesize = 4096;
if(rug::rugged) rug::close();
else rug::init();
return true;
}
if((cmode & sm::NORMAL) && uni == 'r' && on) {
performMarkCommand(mouseover);
return true;
}
if((cmode & sm::NORMAL) && uni == 'R' && on) {
build(crystal::c0, 0);
}
if((cmode & sm::NORMAL) && uni == 'k' && on) {
crystal::view_coordinates = !crystal::view_coordinates;
return true;
}
return false;
}
auto magichook = addHook(hooks_args, 100, readArgs) + addHook(hooks_drawcell, 100, magic_markers)
+ addHook(hooks_mark, 150, magic_rotate)
+ addHook(hooks_handleKey, 150, magic_rugkey);
}
#endif
}
+768
View File
@@ -0,0 +1,768 @@
// 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<string>& v, int cy) {
int Y = isize(v), X = isize(v[0]);
char nx = '6';
for(int y=0; y<Y; y++)
for(int x=0; x<X; x++)
if(v[y][x] != '1')
if((y && v[y-1][x] == '1') || (y<Y-1 && v[y+1][x] == '1') || (x && v[y][x-1] == '1') || (x<X-1 && v[y][x+1] == '1'))
v[y][x] = y < cy ? '4' : y > cy ? '5' : (nx++);
}
vector<string> gensquare(int X, int Y) {
vector<string> res(Y+4, string (X+4, '0'));
for(int y=0; y<Y; y++)
for(int x=0; x<X; x++)
res[y+2][x+2] = '1';
add_border(res, 2+Y/2);
return res;
}
vector<string> gent(int X, int Y) {
vector<string> res(Y+X+4, string (Y+X+X+4, '0'));
for(int y=0; y<Y; y++)
for(int x=0; x<X+X+Y; x++)
res[y+2][x+2] = '1';
for(int y=0; y<Y; y++)
for(int x=0; x<X; x++)
res[x+2+Y][y+2+X] = '1';
add_border(res, 2+Y/2);
return res;
}
vector<string> fmap = gensquare(13, 13);
vector<string> 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<Y; y++)
for(int x=0; x<X; x++) {
if(fmap[y][x] == '6')
vy[y][x] = 0, vx[y][x] = -1;
else if(fmap[y][x] == '7')
vy[y][x] = 0, vx[y][x] = +1;
else if(fmap[y][x] == '4')
vy[y][x] = -1;
else if(fmap[y][x] == '5')
vy[y][x] = +1;
else if(fmap[y][x] == '1')
vy[y][x] = (vy[y-1][x] + vy[y+1][x] + vy[y][x-1] + vy[y][x+1]) / 4;
if(among(fmap[y][x], '1', '4', '5')) {
int qty = 0;
ld total = 0;
auto in = [&] (char c) { return pretty ? c > '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<ld> xes;
for(int y=0; y<Y-1; y++) for(int x=0; x<X-1; x++)
if(fmap[y][x] == '1' && fmap[y+1][x] == '1' && fmap[y][x+1] == '1') {
hyperpoint here = point2(vx[y][x], vy[y][x]);
hyperpoint v0 = point2(vx[y][x+1], vy[y][x+1]) - here;
hyperpoint v1 = point2(vx[y+1][x], vy[y+1][x]) - here;
ld det = (v0 ^ v1)[2];
if(det == 0) continue;
hyperpoint ba2 = point2(v1[1], -v0[1]) / det;
hyperpoint ca2 = point2(-v1[0], v0[0]) / det;
ld d = (ba2|ba2);
if(d == 0) continue;
ld good = (ca2^ba2)[2] / d;
xes.push_back(good);
}
sort(xes.begin(), xes.end());
if(isize(xes))
cscale = -xes[isize(xes) / 2];
// println(hlog, "cscale = ", cscale);
}
#if CAP_NCONF
void nconf_solve() {
nconf::SY = isize(fmap);
nconf::SX = isize(fmap[0]);
nconf::resize_pt();
for(int y=0; y<nconf::SY; y++)
for(int x=0; x<nconf::SX; x++) {
auto& p = nconf::pts[y][x];
p.type = (nconf::ptype)(fmap[y][x] - '0');
p.side = 0;
}
nconf::pretty_borders = pretty;
nconf::draw_progress = false;
nconf::text_progress = false;
nconf::computemap(nconf::pts);
for(int y=0; y<nconf::SY; y++)
for(int x=0; x<nconf::SX; x++) {
if(fmap[y][x] == '1')
vx[y][x] = nconf::pts[y][x].x[0] * 2 - 1,
vy[y][x] = nconf::pts[y][x].x[1] * 2 - 1;
if(pretty && (fmap[y][x] == '4' || fmap[y][x] == '5'))
vx[y][x] = nconf::pts[y][x].x[0] * 2 - 1;
}
printf("nconf solved\n");
}
#endif
basic_textureinfo nctinf, nctinf2;
vector<glvertex> 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<Y; y++) for(int x=0; x<X; x++) if(fmap[y][x] == '6') cc = coord{x,y};
int dir = 0;
while(fmap_at(cc+dir) != '1') dir++;
cc = cc + dir;
dir += 2;
while(true) {
if(fmap_at(cc+dir) == '1') cc = cc + dir, dir++;
else {
if(fmap_at(cc+dir) == '4') fmap_at(cc+dir) = '5';
if(fmap_at(cc+dir) == '7') break;
dir--;
}
}
}
void doublemap() {
for(string& s: fmap) {
string res = "";
for(char c: s) res += c, res += c;
s = res;
}
fmap.resize(isize(fmap) * 2);
for(int y=isize(fmap)-1; 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<Y; y++) for(int x=0; x<X; x++) {
coord cc{x,y};
auto& us = fmap[y][x];
if(us >= '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<Y; y++) for(int x=0; x<X; x++) {
if(fmap[y][x] == '6') {
if(found6) fmap[y][x] = '4'; else found6 = true;
}
if(fmap[y][x] == '7') {
if(found7) fmap[y][x] = '5'; else found7 = true;
}
fix_border();
}
}
int pointmode;
bool show_mapping = true;
bool show_mgrid = true;
ld mapping_split = .75;
char paintmode;
void changepoint(int x, int y, bool can_add) {
int Y = isize(fmap);
int X = isize(fmap[0]);
if(x < 0 || y < 0 || x >= 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 = dialog::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<eModel> pm(pmodel, mdUnchanged);
dynamicval<eGeometry> 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; x<X; x++) for(int y=0; y<Y; y++) {
curvepoint(h(x,y));
curvepoint(h(x+1,y));
curvepoint(h(x+1,y+1));
curvepoint(h(x,y+1));
#if CAP_NCONF
bool ineq =
in_visualization && fmap[y][x] == '1';
#endif
queuecurve(0,
#if CAP_NCONF
(ineq && nconf::pts[y][x].state == 1) ? 0xFF8000FF :
(ineq && nconf::pts[y][x].state == 2) ? 0x00FF00FF :
#endif
(fmap[y][x] == '1' && show_mgrid && show_mapping) ? 0x404040FF : typecols[fmap[y][x] - '0'], PPR::LINE);
}
nctinf.texture_id = rug::glbuf->renderedTexture;
nctinf.tvertices.clear();
static int z = 0;
auto tri = [&] (const array<coord,3>& 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<X-1; x++) for(int y=0; y<Y-1; y++) {
if(fmap[y][x+1] > '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<int, int> 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<X-1; x++) for(int y=0; y<Y-1; y++)
if(fmap[y][x] > '0')
vmap[y][x] = hpxy(vx[y][x]/cscale * sca2 / 2, vy[y][x] * sca2 / 2+ map_ypos);
for(int x=0; x<X-1; x++) for(int y=0; y<Y-1; y++) {
if(y < Y-2 && fmap[y][x] > '0' && fmap[y+1][x] > '0') {
color_t col = (make_pair(x,y) == mpt || make_pair(x,y+1) == mpt) ? 0xFFFF00FF : gridcol;
dynamicval<ld> 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 = (make_pair(x,y) == mpt || make_pair(x+1,y) == mpt) ? 0xFFFF00FF : gridcol;
dynamicval<ld> 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<int> cgl(vid.cells_generated_limit, 9999999);
dynamicval<bool> 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 CAP_NCONF
if(in_visualization)
nconf_run();
else
#endif
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("newconformist"));
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 = dialog::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;
};
}
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();
}
}
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;
}
}
+351
View File
@@ -0,0 +1,351 @@
namespace rogueviz {
#if CAP_ARCM
namespace pentagonal {
transmatrix ts[3];
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)");
check_cgi();
cgi.require_basics();
specialland = laCanvas;
patterns::whichCanvas = 'A';
// vid.wallmode = 1;
printf("start game\n");
printf("distlimit = %d\n", cgi.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)) / degree;
sideangles.emplace_back(alpha, T);
}
}
vector<double> sav;
for(auto p: sideangles) sav.push_back(p.first);
sort(sav.begin(), sav.end());
println(hlog, "sideangles ", sav);
xts0 = tC0(ts[0]);
println(hlog, "original ", 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;
}
ld x, y;
void create_model() {
if(!inHighQual) {
x = (mousex - current_display->xcenter + .0) / vid.xres;
y = (mousey - current_display->ycenter + .0) / vid.yres;
}
int v = global_v;
rug::clear_model();
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);
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);
printf("createmodel with ticks = %d\n", ticks);
transmatrix t = hyperbolic ? hr::cspin(0, 2, M_PI) * xpush(sin(ticks * M_PI * 2 / anims::period)) : hr::cspin(0, 2, ticks * M_PI * 2 / anims::period);
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;
}
}
#if CAP_COMMANDLINE
int readArgs() {
using namespace arg;
if(0) ;
else if(argis("-snub")) {
PHASE(3);
shift();
run_snub(argi(), 3);
}
else if(argis("-snub4")) {
PHASE(3);
shift();
run_snub(argi(), 4);
}
else return 1;
return 0;
}
#endif
bool frame() {
if(snubon && rug::rugged) {
create_model();
nomenukey = true;
displaychr(current_display->xcenter, current_display->ycenter, 0, 10, 'X', 0xFFFFFF);
clearMessages();
nohelp = true;
playerfound = true;
return true;
}
return false;
}
bool handleKey(int sym, int uni) {
if(snubon) {
if(among(uni, '3', '4', '5', '6', '7', '8', '9') && (cmode & sm::NORMAL)) {
run_snub(uni - '0', 3);
return true;
}
if(among(uni, '#', '$', '%', '&', '^') && (cmode & sm::NORMAL)) {
if(uni == '^') uni = '&';
run_snub(uni - 32, 4);
return true;
}
if(uni == 'a' && (cmode & sm::NORMAL)) {
rug::close();
mapeditor::drawplayer = false;
return true;
}
if(uni == 's' && rug::rugged && (cmode & sm::NORMAL)) {
vid.fov += 30;
if(vid.fov >= 180) vid.fov = 60;
}
if(uni == 'i' && rug::rugged && (cmode & sm::NORMAL)) {
rug::invert_depth = !rug::invert_depth;
return true;
}
}
return false;
}
auto xhook = addHook(hooks_args, 100, readArgs)
+ addHook(hooks_handleKey, 0, handleKey)
+ addHook(hooks_prestats, 0, frame)
+ addHook(clearmemory, 40, [] () { snubon = false; } );
}
#endif
}
File diff suppressed because it is too large Load Diff
+130
View File
@@ -0,0 +1,130 @@
// See: http://www.roguetemple.com/z/hyper/rogueviz.php
namespace rogueviz {
using namespace hr;
enum eVizkind { kNONE, kAnyGraph, kTree, kSpiral, kSAG, kCollatz, kFullNet, kKohonen, kFlocking };
extern eVizkind kind;
extern bool on;
void drawExtra();
void close();
void init();
struct edgetype {
double visible_from;
double visible_from_hi;
double visible_from_help;
unsigned color, color_hi;
string name;
};
static const unsigned DEFAULT_COLOR = 0x471293B5;
extern edgetype default_edgetype;
extern vector<shared_ptr<edgetype>> edgetypes;
struct edgeinfo {
int i, j;
double weight, weight2;
vector<glvertex> prec;
basic_textureinfo tinf;
cell *orig;
int lastdraw;
edgetype *type;
edgeinfo(edgetype *t) { orig = NULL; lastdraw = -1; type = t; }
};
struct rvimage {
basic_textureinfo tinf;
texture::texture_data tdata;
vector<hyperpoint> vertices;
};
struct colorpair {
color_t color1, color2;
char shade;
shared_ptr<rvimage> img;
colorpair(color_t col = 0xC0C0C0FF) { shade = 0; color1 = color2 = col; }
};
struct vertexdata {
vector<pair<int, edgeinfo*> > edges;
string name;
colorpair cp;
edgeinfo *virt;
bool special;
int data;
string *info;
shmup::monster *m;
vertexdata() { virt = NULL; m = NULL; info = NULL; special = false; }
};
extern vector<vertexdata> vdata;
void storeall(int from = 0);
namespace anygraph {
extern double R, alpha, T;
extern vector<pair<double, double> > coords;
void fixedges();
void read(string fn, bool subdiv = true, bool doRebase = true, bool doStore = true);
extern int N;
}
extern bool showlabels;
extern bool rog3;
extern bool rvwarp;
#if CAP_TOUR
namespace rvtour {
extern tour::slide rvslides[];
}
#endif
namespace kohonen {
extern int samples;
int showsample(int id);
int showsample(string id);
void describe(cell *c);
void steps();
void showMenu();
bool handleMenu(int sym, int uni);
void clear();
}
namespace staircase {
extern bool on;
void showMenu();
void make_staircase();
}
namespace banachtarski {
extern bool on;
void init_bantar();
void bantar_anim();
extern bool bmap;
extern void init_bantar_map();
}
namespace pentagonal {
void run_snub(int v, int w);
}
extern colorpair dftcolor;
namespace collatz { extern double s2, s3, p2, p3; void start(); }
namespace tree { void read(string fn); }
namespace sag { extern ld edgepower, edgemul;
void read(string fn);
void loadsnake(const string& fn);
}
void readcolor(const string& cfname);
extern bool on;
void close();
extern bool showlabels;
}
+313
View File
@@ -0,0 +1,313 @@
// Hyperbolic Rogue: staircase simulation in RogueViz
// Copyright (C) 2011-2018 Zeno and Tehora Rogue, see 'hyper.cpp' for details
// Kohonen's self-organizing networks.
// This is a part of RogueViz, not a part of HyperRogue.
namespace rogueviz { namespace staircase {
ld scurvature = 0;
ld acurvature = 0;
ld global_r;
ld szoom = 5;
ld progress = 0;
ld strafex, strafey;
hyperpoint spcoord(hyperpoint h) {
ld phi = h[0], y = h[1], z = h[2], r = global_r;
dynamicval<eGeometry> gw(geometry, rug::gwhere == gElliptic ? gSphere : rug::gwhere);
hyperpoint inh = xpush(-acurvature*(y + r - frac(progress))/szoom) * xspinpush0(M_PI/2, acurvature*z);
hyperpoint i = inh * (hdist0(inh) / hypot_d(2, inh));
ld aphi = (r+phi + floor(progress))*M_PI/6;
return hpxyz(i[1] * sin(aphi), i[1] * cos(aphi), i[0]);
}
rug::rugpoint *pt(hyperpoint h, hyperpoint c) {
auto r = rug::addRugpoint(C0, -1);
r->flat = spcoord(h);
r->x1 = c[0];
r->y1 = c[1];
r->valid = true;
return r;
}
void addRect(hyperpoint h, hyperpoint hx, hyperpoint hy, hyperpoint v, hyperpoint vx, hyperpoint vy, int ix, int iy) {
vector<vector<rug::rugpoint*> > rps(iy+1, vector<rug::rugpoint*> (ix+1));
for(int y=0; y<=iy; y++)
for(int x=0; x<=ix; x++) {
rps[y][x] = pt(h+hx*x/ix+hy*y/iy, v+vx*x/ix+vy*y/iy);
}
for(int y=0; y<iy; y++)
for(int x=0; x<ix; x++)
rug::addTriangle(rps[y][x], rps[y+1][x], rps[y][x+1]),
rug::addTriangle(rps[y+1][x+1], rps[y][x+1], rps[y+1][x]);
}
void addTri(hyperpoint h, hyperpoint hx, hyperpoint hy, hyperpoint v, hyperpoint vx, hyperpoint vy, int i) {
vector<vector<rug::rugpoint*> > rps(i+1, vector<rug::rugpoint*> (i+1));
for(int y=0; y<=i; y++)
for(int x=0; x<=i; x++) {
if(x+y <= i)
rps[y][x] = pt(h+hx*x/i+hy*y/i, v+vx*x/i+vy*y/i);
}
for(int y=0; y<i; y++)
for(int x=0; x<i; x++) {
if(x+y < i)
rug::addTriangle(rps[y][x], rps[y+1][x], rps[y][x+1]);
if(x+y+2 <= i)
rug::addTriangle(rps[y+1][x+1], rps[y][x+1], rps[y+1][x]);
}
}
bool need_texture = true;
#if CAP_TEXTURE
texture::texture_data tdata; // = texture::config.data;
#endif
void make_texture() {
#if CAP_TEXTURE
printf("make texture\n");
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++)
pix(0,x,y) = rand();
for(int y=0; y<=fw; y++)
for(int x=0; x<=fw; x++) {
typedef long long ll;
pix(0,x,y) = 0xFF000000 + 0x10101 * ((x*ll(fw-x)*y*(fw-y)*255)/ll(fw/2)/(fw/2)/(fw-fw/2)/(fw-fw/2));
pix(2,x,y) = 0xFF400000 + 0x10000 * (y * 63 / fw);
pix(8,x,y) = 0xFF101010;
pix(10,x,y) = 0xFF000000 + gradient(0, 0xFFD500, 0, x*(fw-x), fw*fw/4);
pix(5,x,y) = 0xFF000000 + gradient(0, 0x804000, -1, sin(2*M_PI*8*y/fw), 1);
pix(7,x,y) = 0xFF000000 + gradient(0, 0x808080, 0, x*ll(fw-x)*y*(fw-y), ll(fw/2)*(fw/2)*(fw-fw/2)*(fw-fw/2));
}
tdata.loadTextureGL();
#endif
}
int savetex;
#if ISWEB
int prec = 1;
int maxr = 100;
#else
int prec = 4;
int maxr = 1000;
#endif
bool on;
void make_staircase() {
// vid.stereo_mode = current_display->sODS;
rug::no_fog = true;
println(hlog, "scurvature = ", scurvature, " progress = ", progress, " strafe=", strafex, ",", strafey);
rug::renderonce = true;
rug::rug_perspective = true;
if(scurvature > -1e-6 && scurvature < 1e-6) {
rug::gwhere = gEuclid;
acurvature = 1;
}
else if(scurvature < 0) {
rug::gwhere = gNormal;
acurvature = -scurvature;
}
else {
rug::gwhere = gSphere;
acurvature = scurvature;
}
rug::ruggospeed = acurvature;
vid.ipd = 0.15 * acurvature;
if(!rug::rugged || !staircase::on) {
staircase::on = true;
rug::reopen();
}
if(need_texture) {
make_texture();
#if CAP_TEXTURE
rug::alternate_texture = tdata.textureid;
#endif
}
rug::clear_model();
printf("compute\n");
for(int r=-maxr; r<maxr; r++) {
if(scurvature < -1e-6 && abs(r * acurvature) > 7*12) continue;
if(scurvature > 1e-6 && abs(acurvature*r/szoom) > M_PI) continue;
global_r = r;
// step
addRect(hpxyz(0,0,1), hpxyz(0,0,1), hpxyz(1,0,0), hpxy(0,0), hpxy(.25,0), hpxy(0,.25), prec, prec);
// step connection
addRect(hpxyz(1,0,1), hpxyz(0,0,1), hpxyz(0,1,0), hpxy(0.75,0.25), hpxy(.25,0), hpxy(0,.25), prec, prec);
// triangle inner side
addTri(hpxyz(1,0,1), hpxyz(0,1,0), hpxyz(-1,0,0), hpxy(.5,0), hpxy(.125,.125), hpxy(0,.125), prec);
// rectangle under triangle
addRect(hpxyz(0,0,1), hpxyz(1,1,0), hpxyz(0,1,0), hpxy(.5,.125), hpxy(.125,0), hpxy(0,.125), prec, prec);
// barrier post
addRect(hpxyz(.45,0,1.1), hpxyz(.1,.1,0), hpxyz(0,-3,0), hpxy(0,.5), hpxy(.25,0), hpxy(0,.25), 2, prec);
// barrier
addRect(hpxyz(.45,-3,1), hpxyz(0,0,.2), hpxyz(1,1,0), hpxy(.5,.5), hpxy(.25,0), hpxy(0,.25), 1, prec);
// outer wall
addRect(hpxyz(0,0,2), hpxyz(1,1,0), hpxyz(0,12,0), hpxy(.25,.25), hpxy(.25,0), hpxy(0,.25), prec, prec);
// lower wall
addRect(hpxyz(0,1,1), hpxyz(1,1,0), hpxyz(0,0,1), hpxy(.5,0), hpxy(.25,0), hpxy(0,.25), prec, prec);
}
printf("push\n");
rug::push_all_points(0, strafex * acurvature);
rug::push_all_points(1, strafey * acurvature);
for(auto p: rug::points) p->valid = true;
rug::good_shape = true;
printf("done (%d points)\n", isize(rug::points));
rug::lowrug = 1e-2 * acurvature;
rug::hirug = 1e3 * acurvature;
}
// -0.50 .. 0.16
int ctick;
void check() {
if(ctick && ctick < ticks) {
calcparam();
make_staircase();
ctick = 0;
}
if(on && !rug::rugged) {
on = false;
rug::alternate_texture = 0;
rug::no_fog = false;
rug::ruggospeed = 1;
staircase::on = false;
}
}
void showMenu() {
cmode = sm::SIDE | sm::MAYDARK;
gamescreen(0);
dialog::init(XLAT("Spiral Staircase"), iinf[itPalace].color, 150, 0);
dialog::addSelItem(" " + XLAT("X"), fts(strafex), 'x');
dialog::addSelItem(" " + XLAT("Y"), fts(strafey), 'y');
dialog::addSelItem(" " + XLAT("curvature"), fts(scurvature), 'c');
dialog::addBreak(100);
dialog::addItem(XLAT("disable menu"), SDLK_ESCAPE);
dialog::addBoolItem(XLAT("low quality"), prec == 1, '1');
dialog::addBoolItem(XLAT("medium quality"), prec == 2, '2');
#if !ISWEB
dialog::addBoolItem(XLAT("high quality"), prec == 4, '3');
#endif
dialog::addItem(XLAT("take me back"), 'q');
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == 'x') {
dialog::editNumber(strafex, -1, 1, .05, 1, XLAT("X"),
XLAT("Also changed with keys A/D")
);
dialog::reaction = [] () { ctick = ticks + 500; };
}
else if(uni == 'y') {
dialog::editNumber(strafey, -1, 1, .05, 1, XLAT("Y"),
XLAT("Also changed with keys W/S")
);
dialog::reaction = [] () { ctick = ticks + 500; };
}
else if(uni == 'c') {
dialog::editNumber(scurvature, -1, 1, .05, 1, XLAT("curvature"),
XLAT("Also changed with keys K/L. Press G for the golden spiral")
);
dialog::reaction = [] () { ctick = ticks + 500; };
}
else if(uni == 'w') {
strafey += .1;
ctick = ticks + 200;
}
else if(uni == 's') {
strafey -= .1;
ctick = ticks + 200;
}
else if(uni == 'd') {
strafex += .1;
ctick = ticks + 200;
}
else if(uni == 'a') {
strafex -= .1;
ctick = ticks + 200;
}
else if(uni == 'k') {
scurvature += .02;
ctick = ticks + 200;
}
else if(uni == 'l') {
scurvature -= .02;
ctick = ticks + 200;
}
else if(uni == 'p') {
progress += .1;
ctick = ticks + 200;
}
else if(uni == 'g') {
scurvature = -4 * log((sqrt(5)+1)/2) / 2.4;
ctick = ticks + 200;
}
else if(uni == '1') {
prec = 1, maxr = 100;
make_staircase();
}
else if(uni == '2') {
prec = 2, maxr = 300;
make_staircase();
}
#if !ISWEB
else if(uni == '3') {
prec = 4, maxr = 1000;
make_staircase();
}
#endif
else if(uni == 'q') {
ctick = 0;
rug::close();
popScreen();
}
else if(doexiton(sym, uni)) popScreen();
};
}
#if CAP_COMMANDLINE
int readArgs() {
using namespace arg;
// #1: load the samples
if(argis("-stair")) {
showstartmenu = false;
ctick = ticks + 2000;
pushScreen(showMenu);
}
else return 1;
return 0;
}
#endif
int phooks = addHook(hooks_args, 100, readArgs)
+ addHook(hooks_fixticks, 100, check);
}}
+92
View File
@@ -0,0 +1,92 @@
// non-Euclidean sunflower spirals (aka golden spirals or Fibonacci spirals)
// Copyright (C) 2018 Zeno and Tehora Rogue, see 'hyper.cpp' for details
// use: commandline parameter -sunflower <quantity> <density>
// e.g.: hyper -sunflower 10000 0.01
// for spherical geometry, density is set automatically to cover the whole sphere
namespace hr {
namespace sunflower {
int qty = 100;
ld density = 1, zdensity;
static const int maxfib = 300000;
hyperpoint ps[maxfib];
hyperpoint p(int i) {
ld step = M_PI * (3 - sqrt(5));
return spin(i * step) * xpush(sphere ? (i+.5) * M_PI / qty : euclid ? sqrt((i+.5) * density) : acosh(1 + (i+.5) * density)) * C0;
}
int inext[maxfib], inext2[maxfib];
const vector<int> fibs =
{1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811};
bool sunflower_cell(cell *c, transmatrix V) {
density = zdensity / 100;
if(c == cwt.at) {
for(int i=0; i<qty; i++) ps[i] = V * p(i);
for(int i=0; i<qty; i++) {
ld ba = 99;
ld bb = 99;
int bi = 0, bj = 0;
for(int a: fibs) {
if(a>i) break;
if(hdist(ps[i], ps[i-a]) < ba)
bb = ba, bj = bi, ba = hdist(ps[i], ps[i-a]), bi = i-a;
else if(hdist(ps[i], ps[i-a]) < bb)
bb = hdist(ps[i], ps[i-a]), bj = i-a;
}
inext[i] = bi;
inext2[i] = bj;
}
for(int i=0; i<qty; i++) {
if(inext[inext[i]] == inext2[i] || inext2[inext[i]] == inext2[i] || inext[inext2[i]] == inext[i] || inext2[inext2[i]] == inext[i]) {
curvepoint(ps[i]);
curvepoint(ps[inext[i]]);
curvepoint(ps[inext2[i]]);
queuecurve(0xFFFFFFFF, 0x00C000FF, PPR::LINE);
}
else {
curvepoint(ps[i]);
curvepoint(ps[inext[i]]);
curvepoint(ps[inext[i] + inext2[i] - i]);
curvepoint(ps[inext2[i]]);
queuecurve(0xFFFFFFFF, 0x0000C0FF, PPR::LINE);
}
}
}
return true;
}
int readArgs() {
using namespace arg;
if(0) ;
else if(argis("-sunflower")) {
shift(); qty = argi();
shift(); zdensity = argf() * 100;
patterns::whichShape = '9';
nohud = true;
addHook(hooks_drawcell, 100, sunflower_cell);
addHook(hooks_mainmenu, 100, [] () {
dialog::addItem("sunflower", 1001);
dialog::add_action([] () { dialog::editNumber(zdensity, 0, 1, .1, 1, "density", "density"); });
});
}
else return 1;
return 0;
}
auto hook = addHook(hooks_args, 100, readArgs);
}
}
+224
View File
@@ -0,0 +1,224 @@
// RogueViz -- source code for creating videos and animations
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
namespace rogueviz {
#if CAP_SDL && CAP_SHOT
// see: https://www.youtube.com/watch?v=4Vu3F95jpQ4&t=6s (Collatz)
// 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)
void rvvideo(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<FRAMECOUNT; i++) {
const char *caption = NULL;
int fade = 255;
bool dt = false, dn = false;
for(int j=0; story[j].text; j++) if(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::setColor(linepatterns::patZebraTriangles, 0x40FF4000 + drawtris);
if(dn && drawnet < 255) drawnet++;
else if(drawnet && !dn) drawnet--;
linepatterns::setColor(linepatterns::patZebraLines, 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;
}
for(int i=0; i<1800; i++) {
char buf[500];
snprintf(buf, 500, fname.c_str(), i);
shmup::pc[0]->base = currentmap->gamestart();
shmup::pc[0]->at = spin(i * 2 * M_PI / (58*30.)) * xpush(1.7);
if(i == 0) drawthemap();
shmup::turn(100);
if(i == 0) drawthemap();
centerpc(100);
printf("%s\n", buf);
shot::take(buf);
}
}
string its05(int i) { char buf[64]; sprintf(buf, "%05d", i); return buf; }
#define TSIZE 4096
// see: https://www.youtube.com/watch?v=HZNRo6mr5pk
void staircase_video(int from, int num, int step) {
resetbuffer rb;
renderbuffer rbuf(TSIZE, TSIZE, true);
vid.stereo_mode = sODS;
for(int i=from; i<num; i+=step) {
ld t = i * 1. / num;
t = pow(t, .3);
staircase::scurvature = t * t * (t-.95) * 4;
staircase::progress = i / 30.;
staircase::strafex = (sin(i / 240.) - sin(i / 501.)) / 2.5;
staircase::strafey = (cos(i / 240.) - cos(i / 501.)) / 2.5;
staircase::make_staircase();
rbuf.enable();
dynamicval<int> vx(vid.xres, TSIZE);
dynamicval<int> vy(vid.yres, TSIZE);
dynamicval<int> vxc(current_display->xcenter, TSIZE/2);
dynamicval<int> vyc(current_display->ycenter, TSIZE/2);
printf("draw scene\n");
rug::drawRugScene();
IMAGESAVE(rbuf.render(), ("staircase/" + its05(i) + IMAGEEXT).c_str());
printf("GL %5d/%5d\n", i, num);
}
rb.reset();
}
#undef TSIZE
#define TSIZE 2048
// see: https://twitter.com/ZenoRogue/status/1001127253747658752
// see also: https://twitter.com/ZenoRogue/status/1000043540985057280 (older version)
void bantar_record() {
resetbuffer rb;
renderbuffer rbuf(TSIZE, TSIZE, true);
int fr = 0;
for(int i=0; i < 10000; i += 33) {
if(i % 1000 == 999) i++;
ticks = i;
rbuf.enable();
vid.xres = vid.yres = TSIZE;
banachtarski::bantar_frame();
IMAGESAVE(rbuf.render(), ("bantar/" + its05(fr) + IMAGEEXT).c_str());
printf("GL %5d/%5d\n", i, 10000);
fr++;
}
rb.reset();
}
#undef TSIZE
#if CAP_COMMANDLINE
int videoArgs() {
using namespace arg;
if(argis("-rvvideo")) {
shift(); rvvideo(arg::args());
}
else if(argis("-staircase_video")) {
staircase_video(0, 128*30, 1); // goal: 168*30
}
else if(argis("-bantar_record")) {
using namespace banachtarski;
PHASE(3);
peace::on = true;
airmap.clear();
ForInfos if(cci.second.c->monst == moAirElemental)
cci.second.c->monst = moFireElemental;
bantar_record();
}
else return 1;
return 0;
}
auto rv_hooks = addHook(hooks_args, 100, videoArgs);
#endif
#endif
}