From 351eda2b5d505568fdb80065108dc97376d2914d Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Fri, 24 Jul 2020 02:30:50 +0200 Subject: [PATCH] generalized PSL to ~SL (discrepancies still appear) --- cell.cpp | 2 +- drawing.cpp | 21 +++-- geom-exp.cpp | 6 +- geometry.cpp | 17 ++-- hypgraph.cpp | 7 +- mapeditor.cpp | 14 +++- nonisotropic.cpp | 199 +++++++++++++++++++++++++++++++++++++---------- system.cpp | 11 ++- util.cpp | 2 + 9 files changed, 211 insertions(+), 68 deletions(-) diff --git a/cell.cpp b/cell.cpp index d560dd9c..c50ef0e6 100644 --- a/cell.cpp +++ b/cell.cpp @@ -115,7 +115,7 @@ transmatrix hrmap::adj(heptagon *h, int i) { return relative_matrix(h->cmove(i), vector& hrmap::allcells() { static vector default_allcells; - if(bounded && !(cgflags & qHUGE_BOUNDED) && !(prod && product::csteps == 0)) { + if(bounded && !(cgflags & qHUGE_BOUNDED) && !(hybri && hybrid::csteps == 0)) { celllister cl(gamestart(), 1000000, 1000000, NULL); default_allcells = cl.lst; return default_allcells; diff --git a/drawing.cpp b/drawing.cpp index 73ba645a..d4cf2b5c 100644 --- a/drawing.cpp +++ b/drawing.cpp @@ -615,6 +615,10 @@ void dqi_poly::gldraw() { if(global_projection && global_projection != ed) continue; current_display->set_all(ed); bool draw = color; + + if(min_slr < max_slr) { + glhr::set_index_sl(band_shift + M_PI * min_slr * hybrid::csteps / cgi.psl_steps); + } flagtype sp = get_shader_flags(); @@ -701,9 +705,8 @@ void dqi_poly::gldraw() { } } - if(min_slr < max_slr) { + if(min_slr+1 < max_slr) { min_slr++; - glhr::set_index_sl(M_PI * min_slr); goto next_slr; } } @@ -1521,15 +1524,19 @@ void dqi_poly::draw() { #if CAP_GL if(vid.usingGL && (current_display->set_all(global_projection), get_shader_flags() & SF_DIRECT)) { - if(sl2 && pmodel == mdGeodesic) { - ld z = atan2(V[2][3], V[3][3]); + if(sl2 && pmodel == mdGeodesic && hybrid::csteps) { + ld z = atan2(V[2][3], V[3][3]) + band_shift; auto zr = sightranges[geometry]; - min_slr = ceil((-zr - z) / M_PI); - max_slr = floor((zr - z) / M_PI); + ld ns = stretch::not_squared(); + ld db = cgi.psl_steps / M_PI / ns / hybrid::csteps; + + min_slr = floor((-zr - z) * db); + max_slr = ceil((zr - z) * db); if(min_slr > max_slr) return; if(flags & POLY_ONE_LEVEL) min_slr = max_slr = 0; - glhr::set_index_sl(M_PI * min_slr); + max_slr++; } + else min_slr = 0, max_slr = 1; set_width(get_width(this)); flags &= ~POLY_INVERSE; gldraw(); diff --git a/geom-exp.cpp b/geom-exp.cpp index 39cc4e9c..6b706552 100644 --- a/geom-exp.cpp +++ b/geom-exp.cpp @@ -569,6 +569,8 @@ EX void select_quotient() { } else if(prod) pushScreen(product::show_config); + else if(rotspace) + hybrid::configure_period(); else { vector choices; for(int i=0; i visited_by_matrix; EX void enqueue_by_matrix(heptagon *h, const transmatrix& T) { if(!h) return; - if(sl2 && T[3][3] < 0) { enqueue_by_matrix(h, centralsym * T); return; } - int b = bucketer(tC0(T)); + int b = bucketer(tC0(T)) + int(floor(band_shift*81527+.5)); if(visited_by_matrix.count(b)) { return; } visited_by_matrix.insert(b); drawqueue.emplace(h, T, band_shift); @@ -2016,8 +2015,7 @@ EX namespace dq { EX void enqueue_by_matrix_c(cell *c, const transmatrix& T) { if(!c) return; - if(sl2 && T[3][3] < 0) { enqueue_by_matrix_c(c, centralsym * T); return; } - int b = bucketer(tC0(T)); + unsigned b = bucketer(tC0(T)) + int(floor(band_shift*81527+.5)); if(visited_by_matrix.count(b)) { return; } visited_by_matrix.insert(b); drawqueue_c.emplace(c, T, band_shift); @@ -2082,6 +2080,7 @@ EX bool do_draw(cell *c, const transmatrix& T) { } else if(pmodel == mdGeodesic && sl2) { if(hypot(tC0(T)[2], tC0(T)[3]) > cosh(slr::range_xy)) return false; + if(band_shift * stretch::not_squared() > sightranges[geometry]) return false; if(!limited_generation(c)) return false; } else if(vid.use_smart_range) { diff --git a/mapeditor.cpp b/mapeditor.cpp index 29d2e880..8f0da82f 100644 --- a/mapeditor.cpp +++ b/mapeditor.cpp @@ -464,9 +464,13 @@ namespace mapstream { f.write(asonov::period_xy); f.write(asonov::period_z); } - if(geometry == gProduct) { - f.write(product::csteps); + if(prod) { + f.write(hybrid::csteps); f.write(product::cspin); + f.write(product::cmirror); + } + if(rotspace) { + f.write(hybrid::csteps); } if(hybri) { hybrid::in_underlying_geometry([&] { save_geometry(f); }); @@ -550,8 +554,12 @@ namespace mapstream { asonov::set_flags(); } if(geometry == gProduct && f.vernum >= 0xA80C) { - f.read(product::csteps); + f.read(hybrid::csteps); if(f.vernum >= 0xA80D) f.read(product::cspin); + if(f.vernum >= 0xA833) f.read(product::cmirror); + } + if(geometry == gRotSpace && f.vernum >= 0xA833) { + f.read(hybrid::csteps); } if(hybri && f.vernum >= 0xA80C) { auto g = geometry; diff --git a/nonisotropic.cpp b/nonisotropic.cpp index 2a5cbe58..c505402d 100644 --- a/nonisotropic.cpp +++ b/nonisotropic.cpp @@ -1065,6 +1065,8 @@ EX namespace hybrid { EX eGeometryClass under_class() { return ginf[hybrid::underlying].cclass; } + EX int csteps; + EX transmatrix ray_iadj(cell *c, int i) { if(prod && i == c->type-2) return (mscale(Id, +cgi.plevel)); if(prod && i == c->type-1) return (mscale(Id, -cgi.plevel)); @@ -1109,7 +1111,7 @@ EX namespace hybrid { ginf[g].g.gameplay_dimension++; ginf[g].g.graphical_dimension++; ginf[g].tiling_name += "xZ"; - if(product::csteps) ginf[g].flags |= qANYQ, ginf[g].tiling_name += its(product::csteps); + if(csteps) ginf[g].flags |= qANYQ, ginf[g].tiling_name += its(csteps); } ginf[g].flags |= qHYBRID; } @@ -1146,6 +1148,73 @@ EX namespace hybrid { map> where; heptagon *getOrigin() override { return underlying_map->getOrigin(); } + + const int SHIFT_UNKNOWN = 30000; + map> shifts; + + EX vector& make_shift(cell *c) { + auto& res = shifts[c]; + if(res.empty()) res = vector (c->type, SHIFT_UNKNOWN); + return res; + } + + EX int& get_shift_current(cell *c, int i) { + return make_shift(c)[i]; + } + + EX bool have_shift(cell *c, int i) { + return shifts.count(c) && get_shift_current(c, i) != SHIFT_UNKNOWN; + } + + EX int get_shift(cell *c, int i) { + auto& v = get_shift_current(c, i); + if(v != SHIFT_UNKNOWN) return v; + + vector candidates; + + for(int a: {1, -1}) { + cellwalker cw0(c, i); + cellwalker cw = cw0; + cw += wstep; cw += a; + int s = 0; + while(cw != cw0) { + if(!have_shift(cw.at, cw.spin)) goto next; + s += shifts[cw.at][cw.spin]; + cw += wstep; + cw += a; + } + candidates.push_back(-a * cgi.single_step - s); + next: ; + } + + if(candidates.size() == 2 && candidates[0] != candidates[1]) { + println(hlog, "discrepancy found ", candidates); + } + + int val = 0; + if(!candidates.empty()) val = candidates[0]; + + v = val; + get_shift_current(c->move(i), c->c.spin(i)) = -val; + + for(int a: {1, -1}) { + cellwalker cw0(c, i); + cellwalker cw = cw0; + cw += wstep; cw += a; + int s = 0; + while(cw != cw0) { + if(!have_shift(cw.at, cw.spin)) goto next1; + s += shifts[cw.at][cw.spin]; + cw += wstep; + cw += a; + } + if(val != -a * cgi.single_step - s) + println(hlog, "incorrect val after setting"); + next1: ; + } + + return val; + } template auto in_underlying(const T& t) -> decltype(t()) { pcgip = cgip; @@ -1162,11 +1231,11 @@ EX namespace hybrid { if(!spins.count(u)) println(hlog, "link missing: ", u); else { - while(h >= cgi.steps) h -= cgi.steps, u = spins[u].first.at; - while(h < 0) h += cgi.steps, u = spins[u].second.at; + while(h >= csteps) h -= csteps, u = spins[u].first.at; + while(h < 0) h += csteps, u = spins[u].second.at; } } - h = zgmod(h, cgi.steps); + h = zgmod(h, csteps); cell*& c = at[make_pair(u, h)]; if(!c) { c = newCell(u->type+2, u->master); where[c] = {u, h}; } return c; @@ -1190,6 +1259,8 @@ EX namespace hybrid { void draw() override { cell* start = centerover; + band_shift = 0; + auto period = (M_PI * csteps) / cgi.psl_steps; dq::visited_by_matrix.clear(); dq::enqueue_by_matrix_c(start, cview()); @@ -1198,6 +1269,7 @@ EX namespace hybrid { auto& p = dq::drawqueue_c.front(); cell *c = get<0>(p); transmatrix V = get<1>(p); + band_shift = get<2>(p); dq::drawqueue_c.pop(); if(!do_draw(c, V)) continue; @@ -1207,7 +1279,24 @@ EX namespace hybrid { for(int i=0; itype; i++) { cell *c1 = c->cmove(i); - dq::enqueue_by_matrix_c(c1, V * adj(c, i)); + transmatrix V1 = V * adj(c, i); + dynamicval bs(band_shift, band_shift); + if(sl2) { + ld alpha = atan2(V1[2][3], V1[3][3]); + band_shift += alpha; + ld ca = cos(alpha), sa = sin(alpha); + if(alpha) for(int a=0; a<4; a++) { + tie(V1[2][a], V1[3][a]) = make_pair(V1[2][a] * ca - V1[3][a] * sa, V1[3][a] * ca + V1[2][a] * sa); + tie(V1[0][a], V1[1][a]) = make_pair(V1[0][a] * ca - V1[1][a] * sa, V1[1][a] * ca + V1[0][a] * sa); + } + if(csteps) { + while(band_shift > period*.4999) + band_shift -= period; + while(band_shift < -period*.5001) + band_shift += period; + } + } + dq::enqueue_by_matrix_c(c1, V1); } } } @@ -1220,19 +1309,28 @@ EX namespace hybrid { } EX pair get_where(cell *c) { return hmap()->where[c]; } - + EX void find_cell_connection(cell *c, int d) { auto m = hmap(); if(d >= c->type - 2) { int s = cgi.single_step; - cell *c1 = get_at(m->where[c].first, m->where[c].second + (d == c->type-1 ? s : -s)); + int lev = m->where[c].second + (d == c->type-1 ? s : -s); + cell *c1 = get_at(m->where[c].first, lev); c->c.connect(d, c1, c1->type - 3 + c->type - d, false); } else { auto cu = m->where[c].first; auto cu1 = m->in_underlying([&] { return cu->cmove(d); }); int d1 = cu->c.spin(d); - int s = (geometry == gRotSpace && cgi.steps) ? d*cgi.steps / cu->type - d1*cgi.steps / cu1->type + cgi.steps/2 : 0; + int s = 0; + if(geometry == gRotSpace) { + if(csteps && cgi.psl_steps % csteps == 0) { + auto ps = cgi.psl_steps; + s = d*ps / cu->type - d1*ps / cu1->type + ps/2; + } + else + s = ((hrmap_hybrid*)currentmap)->get_shift(cu, d); + } cell *c1 = get_at(cu1, m->where[c].second + s); c->c.connect(d, c1, d1, cu->c.mirror(d)); } @@ -1397,7 +1495,7 @@ EX namespace hybrid { auto w1 = hybrid::get_where(c1), w2 = hybrid::get_where(c2); return PIU (hr::celldistance(w1.first, w2.first)); } - else if(cgi.steps == 0) { + else if(csteps == 0) { auto w1 = hybrid::get_where(c1), w2 = hybrid::get_where(c2); return PIU (hr::celldistance(w1.first, w2.first)) + abs(w1.second - w2.second); } @@ -1419,6 +1517,40 @@ EX namespace hybrid { } } + EX void configure_period() { + static int s; + s = csteps / cgi.single_step; + dialog::editNumber(s, 0, 16, 1, 0, XLAT("%1 period", "Z"), ""); + dialog::bound_low(0); + auto set_s = [] (int s1) { + return [s1] { + if(csteps == s1) return; + stop_game(); + csteps = s1 * cgi.single_step; + hybrid::reconfigure(); + start_game(); + }; + }; + dialog::extra_options = [=] () { + if(rotspace) { + int e_steps = cgi.psl_steps / gcd(cgi.single_step, cgi.psl_steps); + dialog::addSelItem( XLAT(sphere ? "elliptic" : "PSL(2,R)"), its(e_steps), 'P'); + dialog::add_action(set_s(e_steps)); + dialog::addSelItem( XLAT(sphere ? "sphere" : "SL(2,R)"), its(2*e_steps), 'P'); + dialog::add_action(set_s(2*e_steps)); + if(sl2) { + dialog::addSelItem( XLAT("universal cover"), its(0), 'P'); + dialog::add_action(set_s(0)); + } + } + else { + dialog::addSelItem( XLAT("non-periodic"), its(0), 'N'); + dialog::add_action(set_s(0)); + } + dialog::reaction_final = set_s(s); + }; + } + EX } EX namespace product { @@ -1427,11 +1559,11 @@ EX namespace product { struct hrmap_product : hybrid::hrmap_hybrid { transmatrix relative_matrix(cell *c2, cell *c1, const hyperpoint& hint) override { - return in_underlying([&] { return calc_relative_matrix(where[c2].first, where[c1].first, hint); }) * mscale(Id, cgi.plevel * szgmod(where[c2].second - where[c1].second, csteps)); + return in_underlying([&] { return calc_relative_matrix(where[c2].first, where[c1].first, hint); }) * mscale(Id, cgi.plevel * szgmod(where[c2].second - where[c1].second, hybrid::csteps)); } transmatrix adj(cell *c, int i) override { - if(twisted && i == c->type-1 && where[c].second == cgi.steps-1) { + if(twisted && i == c->type-1 && where[c].second == hybrid::csteps-1) { auto b = spins[where[c].first].first; transmatrix T = mscale(Id, cgi.plevel); T = T * spin(2 * M_PI * b.spin / b.at->type); @@ -1453,6 +1585,7 @@ EX namespace product { hrmap_product() { current_spin_invalid = false; + using hybrid::csteps; if((cspin || cmirror) && csteps) { in_underlying([&] { twisted = validate_spin(); @@ -1475,11 +1608,9 @@ EX namespace product { } }; - EX bool current_spin_invalid; - - EX int csteps, cspin; - EX bool cmirror; - + EX bool current_spin_invalid, cmirror; + EX int cspin; + EX hyperpoint inverse_exp(hyperpoint h) { hyperpoint res; res[2] = zlevel(h); @@ -1533,43 +1664,30 @@ EX namespace product { } return true; } - + EX void show_config() { cmode = sm::SIDE | sm::MAYDARK; - gamescreen(1); + gamescreen(1); dialog::init(XLAT("quotient product spaces")); - dialog::addSelItem(XLAT("%1 period", "Z"), its(product::csteps), 'z'); - dialog::add_action([] { - static int s; - s = product::csteps; - dialog::editNumber(s, 0, 16, 1, 0, XLAT("%1 period", "Z"), - XLAT("Set to 0 to make it non-periodic.")); - dialog::bound_low(0); - dialog::reaction_final = [] { - product::csteps = s; - if(product::csteps == cgi.steps) return; - hybrid::reconfigure(); - start_game(); - println(hlog, "csteps = ", cgi.steps); - }; - }); - dialog::addSelItem(XLAT("rotation"), its(product::cspin), 'r'); + dialog::addSelItem(XLAT("%1 period", "Z"), its(hybrid::csteps), 'z'); + dialog::add_action(hybrid::configure_period); + dialog::addSelItem(XLAT("rotation"), its(cspin), 'r'); dialog::add_action([] { static int s; dialog::editNumber(s, 0, 16, 1, 0, XLAT("rotation", "Z"), XLAT("Works if the underlying space is symmetric.") ); dialog::reaction_final = [] { - if(s == product::cspin) return; + if(s == cspin) return; stop_game(); - product::cspin = s; + cspin = s; start_game(); }; }); - dialog::addBoolItem(XLAT("reflect"), product::cmirror, 'f'); + dialog::addBoolItem(XLAT("reflect"), cmirror, 'f'); dialog::add_action([]{ stop_game(); - product::cmirror = !product::cmirror; + cmirror = !cmirror; start_game(); }); if(current_spin_invalid) @@ -1797,10 +1915,11 @@ EX namespace slr { "vec4 res = vec4(sqrt(-1.),sqrt(-1.),sqrt(-1.),sqrt(-1.));" "bool flipped = phi > 0.;" - "if(flipped) phi = -phi, h[2] *= -1, h[0] *= -1;" "float alpha = atan2(h[1], -h[0]) + uIndexSL;" + "if(flipped) phi = -phi, alpha = atan2(h[1], h[0]) - uIndexSL;" + "float fiber_barrier = atan(1./uSV);" "float flip_barrier = atan(1. / tanh(asinh(xy)) / uSV);" @@ -2502,7 +2621,7 @@ EX namespace nisot { else if(argis("-prodperiod")) { PHASEFROM(2); if(prod) stop_game(); - shift(); product::csteps = argi(); + shift(); hybrid::csteps = argi(); hybrid::reconfigure(); return 0; } diff --git a/system.cpp b/system.cpp index e59c87a5..377eb2a7 100644 --- a/system.cpp +++ b/system.cpp @@ -119,7 +119,10 @@ EX void welcomeMessage() { else if(nil) addMessage(XLAT("Welcome to NilRogue!")); else if(sl2) { - addMessage(XLAT("Welcome to PSL(2,R)-ogue!")); + if(cgi.psl_steps % hybrid::csteps == 0) + addMessage(XLAT("Welcome to PSL(2,R)-ogue!")); + else + addMessage(XLAT("Welcome to SL(2,R)-ogue!")); if(hybrid::underlying == gNormal && BITRUNCATED) addMessage(XLAT("Hint: this is more playable with pure {7,3} or pure {5,4}")); } @@ -1262,6 +1265,7 @@ EX void set_geometry(eGeometry target) { ors::reset(); if(among(target, gProduct, gRotSpace)) { if(vid.always3) { vid.always3 = false; geom3::apply_always3(); } + if(target == gRotSpace) hybrid::csteps = 0; hybrid::configure(target); } geometry = target; @@ -1296,6 +1300,11 @@ EX void set_geometry(eGeometry target) { if(prod) { pmodel = mdPerspective; if(vid.texture_step < 4) vid.texture_step = 4; } if(WDIM == 3 && (cgflags & qIDEAL) && vid.texture_step < 4) vid.texture_step = 4; if(sl2) nisot::geodesic_movement = true; + + if(rotspace) { + check_cgi(); cgi.require_basics(); + hybrid::csteps = cgi.psl_steps; + } } } diff --git a/util.cpp b/util.cpp index 5f6748ee..2055dd2a 100644 --- a/util.cpp +++ b/util.cpp @@ -370,6 +370,8 @@ cld exp_parser::parse(int prio) { else if(number == "mousex") res = mousex; else if(number == "deg") res = degree; else if(number == "ultra_mirror_dist") res = cgi.ultra_mirror_dist; + else if(number == "psl_steps") res = cgi.psl_steps; + else if(number == "single_step") res = cgi.single_step; else if(number == "step") res = hdist0(tC0(currentmap->adj(cwt.at, 0))); else if(number == "mousey") res = mousey; else if(number == "random") res = randd();