From 136b93160985fb2274fce69b940e577a41bfcc0f Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Sat, 24 Aug 2019 11:55:45 +0200 Subject: [PATCH] PSL(2,R) --- 3d-models.cpp | 4 +- basegraph.cpp | 2 + bigstuff.cpp | 2 +- cell.cpp | 2 + classes.cpp | 2 + classes.h | 4 +- complex.cpp | 6 +- config.cpp | 2 + control.cpp | 11 +- floorshapes.cpp | 3 +- geom-exp.cpp | 4 + geometry.cpp | 20 ++- graph.cpp | 13 +- hyper.h | 3 +- hyperpoint.cpp | 16 +- hypgraph.cpp | 27 +++- nonisotropic.cpp | 385 ++++++++++++++++++++++++++++++++++++++++++++++- polygons.cpp | 46 +++++- rug.cpp | 2 +- shaders.cpp | 9 +- system.cpp | 4 +- 21 files changed, 525 insertions(+), 42 deletions(-) diff --git a/3d-models.cpp b/3d-models.cpp index bd49c12e..55c38384 100644 --- a/3d-models.cpp +++ b/3d-models.cpp @@ -615,8 +615,8 @@ void geometry_information::animate_bird(hpcshape& orig, hpcshape_animated& anima // shift_shape(orig, BIRD); } -EX hyperpoint forward_dir(ld x) { return prod ? point3(x, 0, 0) : xpush0(x); } -EX hyperpoint zforward_dir(ld z) { return prod ? point3(0, 0, z) : zpush0(z); } +EX hyperpoint forward_dir(ld x) { return (prod || sl2) ? point3(x, 0, 0) : xpush0(x); } +EX hyperpoint zforward_dir(ld z) { return (prod || sl2) ? point3(0, 0, z) : zpush0(z); } EX hyperpoint dir_to_point(hyperpoint h) { return prod ? product::direct_exp(h) : h; } diff --git a/basegraph.cpp b/basegraph.cpp index 9196d78f..4aa4e931 100644 --- a/basegraph.cpp +++ b/basegraph.cpp @@ -303,6 +303,8 @@ void display_data::set_projection(int ed) { shaderside_projection = true, glhr::new_shader_projection = glhr::shader_projection::standardEH2, pers3 = true; if(GDIM == 3 && apply_models && pmodel == mdGeodesic && sol) shaderside_projection = true, glhr::new_shader_projection = glhr::shader_projection::standardSolv, pers3 = true; + if(GDIM == 3 && apply_models && pmodel == mdGeodesic && sl2) + shaderside_projection = true, glhr::new_shader_projection = glhr::shader_projection::standardSL2, pers3 = true; if(GDIM == 3 && apply_models && pmodel == mdGeodesic && nil) shaderside_projection = true, glhr::new_shader_projection = glhr::shader_projection::standardNil, pers3 = true; if(GDIM == 3 && sphere && apply_models && pmodel == mdPerspective) { diff --git a/bigstuff.cpp b/bigstuff.cpp index d275c613..a9e57a87 100644 --- a/bigstuff.cpp +++ b/bigstuff.cpp @@ -246,7 +246,7 @@ EX heptagon *createAlternateMap(cell *c, int rad, hstate firststate, int special alt->emeraldval = 0; alt->zebraval = 0; alt->distance = 0; - alt->fieldval = product::current_view_level; + alt->fieldval = nisot::current_view_level; alt->c7 = NULL; alt->alt = alt; h->alt = alt; diff --git a/cell.cpp b/cell.cpp index f0693e6a..35d89ee6 100644 --- a/cell.cpp +++ b/cell.cpp @@ -148,6 +148,8 @@ EX cell *createMov(cell *c, int d) { if(c->move(d)) return c->move(d); else if(geometry == gProduct) product::find_cell_connection(c, d); + else if(sl2) + slr::find_cell_connection(c, d); #if CAP_BT else if(penrose) kite::find_cell_connection(c, d); diff --git a/classes.cpp b/classes.cpp index fad22cc3..df4ebabc 100644 --- a/classes.cpp +++ b/classes.cpp @@ -526,6 +526,7 @@ geometryinfo1 giSphere3 = { gcSphere, 3, 3, 4, {1,1, 1,+1} }; geometryinfo1 giSol = { gcSol, 3, 3, 4, {1,1, 1,0 } }; geometryinfo1 giNil = { gcNil, 3, 3, 4, {1,1, 1,0 } }; geometryinfo1 giProduct = { /* will be filled in product::configure() */ }; +geometryinfo1 giSL2 = { gcSL2, 3, 3, 4, {1,1,-1,-1} }; /** list of available geometries */ vector ginf = { @@ -584,6 +585,7 @@ vector ginf = { {"kd3", "none", "kite-and-dart on horospheres", "kd3", 12, 3, qsBP, giHyperb3, 0x48200, {{7, 3}}, eVariation::pure}, {"nil", "none", "Nil geometry", "nil", 8, 3, 0, giNil, 0x48600, {{7, 5}}, eVariation::pure}, {"product","none", "product space", "product", 7, 3, 0, giProduct, 0x48400, {{7, 3}}, eVariation::pure}, + {"psl2", "psl2", "PSL(2,R)", "psl2", 7, 3, qEXPERIMENTAL, giSL2, 0x49000, {{6, 4}}, eVariation::pure}, }; // bits: 9, 10, 15, 16, (reserved for later) 17, 18 diff --git a/classes.h b/classes.h index 42d84d64..60c19e3f 100644 --- a/classes.h +++ b/classes.h @@ -214,10 +214,10 @@ enum eGeometry { gHoroTris, gHoroRec, gHoroHex, gField435, gField534, gBinary4, gSol, - gKiteDart2, gKiteDart3, gNil, gProduct, + gKiteDart2, gKiteDart3, gNil, gProduct, gSL2, gGUARD}; -enum eGeometryClass { gcHyperbolic, gcEuclid, gcSphere, gcSol, gcNil, gcProduct }; +enum eGeometryClass { gcHyperbolic, gcEuclid, gcSphere, gcSol, gcNil, gcProduct, gcSL2 }; enum class eVariation { bitruncated, pure, goldberg, irregular, dual }; diff --git a/complex.cpp b/complex.cpp index e6023924..b4c4716f 100644 --- a/complex.cpp +++ b/complex.cpp @@ -2668,7 +2668,7 @@ EX namespace sword { }; /** dimensions available to the Sword */ - #define SWORDDIM (prod ? 2 : WDIM) + #define SWORDDIM ((prod || sl2) ? 2 : WDIM) #endif @@ -2695,7 +2695,7 @@ EX namespace sword { cell *pos2(cell *c, int s) { int t = c->type; - if(prod) t -= 2; + if(prod || sl2) t -= 2; s *= 2; s += sword_angles/t; s %= (2 * sword_angles); @@ -2748,7 +2748,7 @@ EX namespace sword { int s2 = neighborId(c2, c1); if(s1 < 0 || s2 < 0) return d; if(SWORDDIM == 2) { - int sub = prod ? 2 : 0; + int sub = (prod || sl2) ? 2 : 0; int t2 = c2->type - sub; int t1 = c1->type - sub; if(c1->c.mirror(s1)) diff --git a/config.cpp b/config.cpp index 90ac70a0..ec26e1dd 100644 --- a/config.cpp +++ b/config.cpp @@ -520,6 +520,8 @@ EX void initConfig() { sightranges[i] = 2 * M_PI; else if(ginf[i].cclass == gcEuclid) sightranges[i] = 10; + else if(ginf[i].cclass == gcSL2) + sightranges[i] = 1.5; else sightranges[i] = 5; sightranges[gArchimedean] = 10; diff --git a/control.cpp b/control.cpp index 49073450..39529a6f 100644 --- a/control.cpp +++ b/control.cpp @@ -284,6 +284,13 @@ typedef SDL_Event eventtype; EX bool smooth_scrolling = false; +transmatrix zforward_push(ld z) { + if(!sl2) return zpush(z); + transmatrix T = Id; + T[2][3] = z; + return T; + } + EX void handlePanning(int sym, int uni) { auto& LPe = nisot::local_perspective; if(mousepan && dual::split([=] { handlePanning(sym, uni); })) return; @@ -300,10 +307,10 @@ EX void handlePanning(int sym, int uni) { #if !ISPANDORA if(sym == SDLK_END && GDIM == 3) { - View = solmul(cpush(2, -0.2*shiftmul), LPe, View), didsomething = true, playermoved = false; + View = solmul(zforward_push(-0.2*shiftmul), LPe, View), didsomething = true, playermoved = false; } if(sym == SDLK_HOME && GDIM == 3) { - View = solmul(cpush(2, +0.2*shiftmul), LPe, View), didsomething = true, playermoved = false; + View = solmul(zforward_push(+0.2*shiftmul), LPe, View), didsomething = true, playermoved = false; } if(sym == SDLK_RIGHT) { if(history::on) diff --git a/floorshapes.cpp b/floorshapes.cpp index b02f90b2..fbe120d4 100644 --- a/floorshapes.cpp +++ b/floorshapes.cpp @@ -942,7 +942,8 @@ void draw_shape_for_texture(floorshape* sh) { hyperpoint v1 = hpxyz3(0.25, 0.25, 0, 0); hyperpoint v2 = hpxyz3(0.25, -0.25, 0, 0); - for(int a=0; a g(geometry, gNormal); + check_cgi(); + cgi.prepare_basics(); + rhexf = cgi.rhexf/2; + hexf = cgi.hexf/2; + crossf = cgi.crossf/2; + hcrossf = cgi.hcrossf/2; + tessf = cgi.tessf/2; + hexvdist = cgi.hexvdist/2; + hexhexdist = cgi.hexhexdist/2; + base_distlimit = cgi.base_distlimit-1; + cgip = this; + goto prod_finish; + } if((sphere || hyperbolic) && WDIM == 3 && !binarytiling) { rhexf = hexf = 0.378077; diff --git a/graph.cpp b/graph.cpp index 86a3be3c..840b8277 100644 --- a/graph.cpp +++ b/graph.cpp @@ -4426,7 +4426,7 @@ color_t transcolor(cell *c, cell *c2, color_t wcol) { // how much should be the d-th wall darkened in 3D int get_darkval(cell *c, int d) { - if(prod) { + if(prod || sl2) { return d >= c->type - 2 ? 4 : 0; } const int darkval_hbt[9] = {0,2,2,0,6,6,8,8,0}; @@ -4518,7 +4518,7 @@ EX int noclipped; void make_clipping_planes() { #if MAXMDIM >= 4 clipping_planes.clear(); - if(PIU(sphere)) return; + if(PIU(sphere) || experimental) return; auto add_clipping_plane = [] (ld x1, ld y1, ld x2, ld y2) { ld z1 = 1, z2 = 1; hyperpoint sx = point3(y1 * z2 - y2 * z1, z1 * x2 - z2 * x1, x1 * y2 - x2 * y1); @@ -6031,7 +6031,7 @@ EX void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { for(int a=0; atype; a++) if(c->move(a) && !isWall3(c->move(a), dummy)) { - if(pmodel == mdPerspective && !sphere && !quotient && !penrose && !nonisotropic && !prod) { + if(pmodel == mdPerspective && !sphere && !quotient && !penrose && !nonisotropic && !prod && !experimental) { if(a < 4 && among(geometry, gHoroTris, gBinary3) && celldistAlt(c) >= celldistAlt(viewcenter())) continue; else if(a < 2 && among(geometry, gHoroRec) && celldistAlt(c) >= celldistAlt(viewcenter())) continue; else if(c->move(a)->master->distance > c->master->distance && c->master->distance > viewctr.at->distance && !quotient) continue; @@ -6364,8 +6364,8 @@ EX void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { floorShadow(c, V, SHADOW_SL * sl); for(int s=0; s= sl2) + int sl_2 = snakelevel(c2); + if(s >= sl_2) if(placeSidewall(c, i, SIDE_SLEV+s, V, getSnakelevColor(c, s, sl, fd, wcol))) break; } } @@ -8043,7 +8043,8 @@ EX void drawBug(const cellwalker& cw, color_t col) { EX cell *viewcenter() { if(masterless) return centerover.at; - else if(prod) return product::get_at(viewctr.at->c7, product::current_view_level); + else if(prod) return product::get_at(viewctr.at->c7, nisot::current_view_level); + else if(sl2) return slr::get_at(viewctr.at->c7, nisot::current_view_level); else return viewctr.at->c7; } diff --git a/hyper.h b/hyper.h index d7115022..4d8ad176 100644 --- a/hyper.h +++ b/hyper.h @@ -125,9 +125,10 @@ void addMessage(string s, char spamtype = 0); #define sphere (cgclass == gcSphere) #define sol (cgclass == gcSol) #define nil (cgclass == gcNil) +#define sl2 (cgclass == gcSL2) #define prod (cgclass == gcProduct) #define hyperbolic (cgclass == gcHyperbolic) -#define nonisotropic (sol || nil) +#define nonisotropic (sol || nil || sl2) #define translatable (euclid || nonisotropic) #define nonorientable (ginf[geometry].flags & qNONORIENTABLE) #define elliptic (ginf[geometry].flags & qELLIPTIC) diff --git a/hyperpoint.cpp b/hyperpoint.cpp index 5dda8250..eabf4242 100644 --- a/hyperpoint.cpp +++ b/hyperpoint.cpp @@ -355,7 +355,8 @@ EX ld hypot_d(int d, const hyperpoint& h) { } EX ld zlevel(const hyperpoint &h) { - if(translatable) return h[LDIM]; + if(sl2) return sqrt(-intval(h, Hypc)); + else if(translatable) return h[LDIM]; else if(sphere) return sqrt(intval(h, Hypc)); else if(prod) return log(sqrt(abs(intval(h, Hypc)))); /* abs works with both underlying spherical and hyperbolic */ else return (h[LDIM] < 0 ? -1 : 1) * sqrt(-intval(h, Hypc)); @@ -444,6 +445,7 @@ EX transmatrix eupush(hyperpoint h) { } EX transmatrix eupush3(ld x, ld y, ld z) { + if(sl2) return slr::translate(slr::xyz_point(x, y, z)); return eupush(point3(x, y, z)); } @@ -827,6 +829,11 @@ EX ld hdist0(const hyperpoint& mh) { auto d1 = product_decompose(mh); return hypot(PIU(hdist0(d1.second)), d1.first); } + case gcSL2: { + auto cosh_r = hypot(mh[2], mh[3]); + auto phi = atan2(mh[2], mh[3]); + return hypot(cosh_r < 1 ? 0 : acosh(cosh_r), phi); + } default: return hypot_d(GDIM, mh); } @@ -1017,8 +1024,14 @@ EX transmatrix transpose(transmatrix T) { } #if HDR +namespace slr { + hyperpoint xyz_point(ld x, ld y, ld z); + hyperpoint polar(ld r, ld theta, ld phi); + } + inline hyperpoint cpush0(int c, ld x) { hyperpoint h = Hypc; + if(sl2) return slr::xyz_point(c==0?x:0, c==1?x:0, c==2?x:0); if(c == 2 && prod) { h[2] = exp(x); return h; @@ -1029,6 +1042,7 @@ inline hyperpoint cpush0(int c, ld x) { } inline hyperpoint xspinpush0(ld alpha, ld x) { + if(sl2) return slr::polar(x, -alpha, 0); hyperpoint h = Hypc; h[LDIM] = cos_auto(x); h[0] = sin_auto(x) * cos(alpha); diff --git a/hypgraph.cpp b/hypgraph.cpp index 018f4896..b88bd252 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -1382,13 +1382,30 @@ EX void optimizeview() { if(subscreens::split(optimizeview)) return; if(dual::split(optimizeview)) return; + + if(sl2) { + cell *c = viewcenter(); + cell *cbest = NULL; + ld best = hdist0(tC0(gmatrix[c])); + if(isnan(best)) return; + forCellEx(c2, c) { + ld quality = hdist0(tC0(gmatrix[c2])); + if(quality < best) best = quality, cbest = c2; + } + if(cbest) { + View = View * currentmap->relative_matrix(cbest, c, C0); + viewctr.at = cbest->master; + nisot::current_view_level = slr::get_where(cbest).second; + } + return; + } if(prod) { ld z = zlevel(tC0(View)); View = mscale(View, -z); product::in_underlying_map(optimizeview); - if(z > cgi.plevel / 2) { product::current_view_level--; z -= cgi.plevel; } - if(z < -cgi.plevel / 2) { product::current_view_level++; z += cgi.plevel; } + if(z > cgi.plevel / 2) { nisot::current_view_level--; z -= cgi.plevel; } + if(z < -cgi.plevel / 2) { nisot::current_view_level++; z += cgi.plevel; } View = mscale(View, z); return; } @@ -1483,7 +1500,7 @@ EX void resetview() { else centerover = cwt; cwtV = View; nisot::local_perspective = Id; - if(prod) product::current_view_level = product::get_where(cwt.at).second; + if(prod || sl2) nisot::current_view_level = product::get_where(cwt.at).second; // SDL_LockSurface(s); // SDL_UnlockSurface(s); } @@ -1970,7 +1987,7 @@ bool limited_generation(cell *c) { EX bool do_draw(cell *c, const transmatrix& T) { - if(product::pmap) return product::in_actual([&] { return do_draw(product::get_at(c, product::current_view_level), T); }); + if(product::pmap) return product::in_actual([&] { return do_draw(product::get_at(c, nisot::current_view_level), T); }); if(WDIM == 3) { if(cells_drawn > vid.cells_drawn_limit) return false; if(nil && pmodel == mdGeodesic) { @@ -1978,7 +1995,7 @@ EX bool do_draw(cell *c, const transmatrix& T) { if(dist > sightranges[geometry] + (vid.sloppy_3d ? 0 : 0.9)) return false; if(dist <= extra_generation_distance && !limited_generation(c)) return false; } - else if(pmodel == mdGeodesic && !nil) { + else if(pmodel == mdGeodesic && sol) { if(!nisot::in_table_range(tC0(T))) return false; if(!limited_generation(c)) return false; } diff --git a/nonisotropic.cpp b/nonisotropic.cpp index 44e6c816..2ea984b2 100644 --- a/nonisotropic.cpp +++ b/nonisotropic.cpp @@ -10,6 +10,7 @@ namespace hr { EX namespace nisot { typedef array ptlow; + EX int current_view_level; EX transmatrix local_perspective; #if HDR inline bool local_perspective_used() { return nonisotropic || prod; } @@ -18,6 +19,8 @@ EX namespace nisot { EX bool geodesic_movement = true; EX transmatrix translate(hyperpoint h) { + if(sl2) + return slr::translate(h); transmatrix T = Id; for(int i=0; itype) - 1; else { @@ -652,7 +653,7 @@ EX namespace product { for(int z=-max_z; z<=max_z; z++) { if(z == 0) cwall_mask ^= (2<type); if(z == 1) cwall_mask ^= (1<type); - cell *c1 = get_at(c, current_view_level+z); + cell *c1 = get_at(c, nisot::current_view_level+z); setdist(c1, 7, NULL); drawcell(c1, V * mscale(Id, cgi.plevel * z), spinv, mirrored); } @@ -769,11 +770,360 @@ EX namespace product { EX } +EX namespace slr { + + /* This implementation is based on: + // https://pdfs.semanticscholar.org/bf46/824df892593a1b6d1c84a5f99e90eece7c54.pdf + // However, to make it consistent with the conventions in HyperRogue, + // coordinates 0<->2 and 1<->3 are swapped, + // then coordinates 2<->3 are swapped + */ + + EX hyperpoint from_phigans(hyperpoint h) { + ld r = asinh(hypot_d(2, h)); + ld x = h[0]; + ld y = h[1]; + ld z = h[2]; + return hyperpoint(x * cos(z) + y * sin(z), y * cos(z) - x * sin(z), cosh(r) * sin(z), cosh(r) * cos(z)); + } + + EX hyperpoint to_phigans(hyperpoint h) { + ld z = atan2(h[2], h[3]); + ld x = h[0]; + ld y = h[1]; + return point31(x * cos(z) - y * sin(z), y * cos(z) + x * sin(z), z); + } + + /** in the 'phigans' model */ + hyperpoint christoffel(const hyperpoint Position, const hyperpoint Velocity, const hyperpoint Transported) { + ld x = Position[0]; + ld y = Position[1]; + ld s = x*x + y*y + 1; + ld x2 = x * x; + ld y2 = y * y; + ld x4 = x2 * x2; + ld y4 = y2 * y2; + return point3( + + Velocity[ 0 ] * Transported[ 0 ] * (x*(4*x2*y2 + 4*y4 + 9*y2 + 1)) + + Velocity[ 0 ] * Transported[ 1 ] * (-y*(4*x4 + 4*x2*y2 + 9*x2 + 2)) + + Velocity[ 0 ] * Transported[ 2 ] * (-x*y*(x2 + y2 + 2)) + + Velocity[ 1 ] * Transported[ 0 ] * (-y*(4*x4 + 4*x2*y2 + 9*x2 + 2)) + + Velocity[ 1 ] * Transported[ 1 ] * (x*(4*x4 + 4*x2*y2 + 9*x2 + 5)) + + Velocity[ 1 ] * Transported[ 2 ] * (x4 + x2*y2 + 2*x2 + 1) + + Velocity[ 2 ] * Transported[ 0 ] * (-x*y*(x2 + y2 + 2)) + + Velocity[ 2 ] * Transported[ 1 ] * (x4 + x2*y2 + 2*x2 + 1), + + + Velocity[ 0 ] * Transported[ 0 ] * (y*(4*x2*y2 + 4*y4 + 9*y2 + 5)) + + Velocity[ 0 ] * Transported[ 1 ] * (-x*(4*x2*y2 + 4*y4 + 9*y2 + 2)) + + Velocity[ 0 ] * Transported[ 2 ] * (-x2*y2 - y4 - 2*y2 - 1) + + Velocity[ 1 ] * Transported[ 0 ] * (-x*(4*x2*y2 + 4*y4 + 9*y2 + 2)) + + Velocity[ 1 ] * Transported[ 1 ] * (y*(4*x4 + 4*x2*y2 + 9*x2 + 1)) + + Velocity[ 1 ] * Transported[ 2 ] * (x*y*(x2 + y2 + 2)) + + Velocity[ 2 ] * Transported[ 0 ] * (-x2*y2 - y4 - 2*y2 - 1) + + Velocity[ 2 ] * Transported[ 1 ] * (x*y*(x2 + y2 + 2)), + + + Velocity[ 0 ] * Transported[ 0 ] * (-4*x*y) + + Velocity[ 0 ] * Transported[ 1 ] * (2*x2 - 2*y2) + + Velocity[ 0 ] * Transported[ 2 ] * x + + Velocity[ 1 ] * Transported[ 0 ] * (2*x2 - 2*y2) + + Velocity[ 1 ] * Transported[ 1 ] * 4*x*y + + Velocity[ 1 ] * Transported[ 2 ] * y + + Velocity[ 2 ] * Transported[ 0 ] * x + + Velocity[ 2 ] * Transported[ 1 ] * y + ) / s; + } + + EX transmatrix translate(hyperpoint h) { + return matrix4( + h[3], -h[2], h[1], h[0], + h[2], h[3], -h[0], h[1], + h[1], -h[0], h[3], h[2], + h[0], h[1], -h[2], h[3] + ); + } + + EX hyperpoint polar(ld r, ld theta, ld phi) { + return hyperpoint(sinh(r) * cos(theta-phi), sinh(r) * sin(theta-phi), cosh(r) * sin(phi), cosh(r) * cos(phi)); + } + + EX hyperpoint xyz_point(ld x, ld y, ld z) { + ld r = hypot(x, y); + ld f = r ? sinh(r) / r : 1; + return hyperpoint(x * f * cos(z) + y * f * sin(z), y * f * cos(z) - x * f * sin(z), cosh(r) * sin(z), cosh(r) * cos(z)); + } + + ld rootsin(ld square, ld s) { + if(square > 0) return sinh(sqrt(square) * s) / sqrt(square); + else if(square < 0) return sin(sqrt(-square) * s) / sqrt(-square); + else return s; + } + + /** it==0 is standard asin, it==1 is the next solution (PI-asin) */ + ld asin_it(ld z, int it) { + auto ans = asin(z); + if(it & 1) ans = M_PI - ans; + return ans; + } + + ld arootsin(ld square, ld v, int it) { + if(square > 0) return asinh(v * sqrt(square)) / sqrt(square); + else if(square < 0) return asin_it(v * sqrt(-square), it) / sqrt(-square); + else return v; + } + + ld roottan(ld square, ld s) { + if(square > 0) return tanh(sqrt(square) * s) / sqrt(square); + else if(square < 0) return tan(sqrt(-square) * s) / sqrt(-square); + else return s; + } + + hyperpoint geodesic_polar(ld alpha, ld beta, ld s) { + auto c = cos(2*alpha); + + ld t; + if(c > 0) + t = atan(sin(alpha) * tanh(sqrt(c) * s) / sqrt(c)); + else if(c < 0) { + /* the formula in the paper is roughly atan(k*tan(s)) + * however, atan is not always to be taken in [-PI/2,PI/2]: + * if s is in [kPI-PI/2, kPI+PI/2], we should also increase the result by kPI + */ + ld x = sqrt(-c) * s; + ld steps = floor(x/M_PI + 0.5); + t = atan(sin(alpha) * tan(sqrt(-c) * s) / sqrt(-c)) + M_PI * steps; + } + else t = atan(sin(alpha) * s); + + return polar( + asinh(cos(alpha) * rootsin(c, s)), + beta - t, + 2*sin(alpha)*s - t + ); + } + + EX hyperpoint formula_exp(hyperpoint h) { + ld s = hypot_d(3, h); + ld beta = atan2(h[1], h[0]); + ld alpha = asin(h[2] / s); + return geodesic_polar(alpha, beta, s); + } + + void find_alpha(ld phi, ld r, ld theta, ld &alpha, ld &s, ld &beta) { + if(phi < 0) { find_alpha(-phi, r, -theta, alpha, s, beta); alpha = -alpha; beta = -beta; return; } + ld mina = 0, maxa = M_PI/2; + + bool next_nan; + ld c; + + for(int it=0; it<40; it++) { + alpha = (mina + maxa) / 2; + + c = cos(2 * alpha); + s = arootsin(c, sinh(r) / cos(alpha), 0); + if(isnan(s)) { next_nan = true, maxa = alpha; continue; } + ld got_phi = 2*sin(alpha)*s - atan(sin(alpha) * roottan(c, s)); + if(got_phi > phi) next_nan = false, maxa = alpha; + else mina = alpha; + } + + if(next_nan) { + mina = M_PI/4; + + for(int it=0; it<40; it++) { + alpha = (mina + maxa) / 2; + c = cos(2 * alpha); + s = arootsin(c, sinh(r) / cos(alpha), 1); + ld got_phi = 2*sin(alpha)*s - atan(sin(alpha) * roottan(c, s)) - M_PI; + if(got_phi < phi) maxa = alpha; + else mina = alpha; + } + beta = theta + atan(sin(alpha) * roottan(c, s)) + M_PI; + } + else beta = theta + atan(sin(alpha) * roottan(c, s)); + } + + EX hyperpoint get_inverse_exp(hyperpoint h) { + if(sqhypot_d(2, h) < 1e-12) return point3(0, 0, atan2(h[2], h[3])); + ld r = asinh(hypot_d(2, h)); + ld phi = atan2(h[2], h[3]); + ld theta = atan2(h[1], h[0]) + phi; + + ld alpha, s, beta; + find_alpha(phi, r, theta, alpha, s, beta); + + return point3(s * cos(beta) * cos(alpha), s * sin(beta) * cos(alpha), s * sin(alpha)); + } + + EX string slshader = + "float atan2(float y, float x) {" + " if(x == 0.) return y > 0. ? PI/2. : -PI/2.;" + " if(x > 0.) return atan(y / x);" + " if(y >= 0.) return atan(y / x) + PI;" + " if(y < 0.) return atan(y / x) - PI;" + " }" + + "vec4 inverse_exp(vec4 h) {" + "if(h[0]*h[0] + h[1] * h[1] < 1e-6) return vec4(0, 0, atan(h[2], h[3]), 1);" + "float r = asinh(sqrt(h[0] * h[0] + h[1] * h[1]));" + "float phi = atan2(h[2], h[3]);" + "float theta = atan2(h[1], h[0]) + phi;" + "float alpha;" + "float s;" + "float beta;" + "float sgn = 1.;" + "if(phi < 0.) { phi = -phi; theta = -theta; sgn = -1.; }" + "float c;" + "s = sinh(r) / cos(PI/4.);" + "float gphi = 2.*sin(PI/4.)*s - atan(sin(PI/4.) * s);" + "if(gphi > phi) {" + " float mina = 0.;" + " float maxa = PI/4.;" + " for(int it=0; it<40; it++) {" + " alpha = (mina + maxa) / 2.;" + " c = sqrt(cos(2. * alpha));" + " s = asinh(sinh(r) / cos(alpha) * c) / c;" + " gphi = 2.*sin(alpha)*s - atan(sin(alpha) * tanh(c * s) / c);" + " if(gphi > phi) maxa = alpha;" + " else mina = alpha;" + " }" + " beta = theta + atan(sin(alpha) * tanh(c * s) / c);" + " }" + "else {" + " int next_nan = 1;" + " float mina = PI/4.;" + " float maxa = PI/2.;" + " for(int it=0; it<40; it++) {" + " alpha = (mina + maxa) / 2.;" + " c = sqrt(-cos(2. * alpha));" + " if(sinh(r) * c > cos(alpha)) { next_nan = 1; maxa = alpha; continue; }" + " s = asin(sinh(r) * c / cos(alpha)) / c;" + " gphi = 2.*sin(alpha)*s - atan(sin(alpha) * tan(c*s) / c);" + " if(gphi > phi) { next_nan = 0; maxa = alpha; }" + " else mina = alpha;" + " }" + " if(next_nan != 0) {" + " mina = PI/4.;" + " for(int it=0; it<10; it++) {" + " alpha = (mina + maxa) / 2.;" + " c = sqrt(-cos(2. * alpha));" + " s = PI - asin(sinh(r) * c / cos(alpha)) / c;" + " gphi = 2.*sin(alpha)*s - atan(sin(alpha) * tan(c*s) / c) - PI;" + " if(gphi < phi) maxa = alpha; else mina = alpha;" + " }" + " beta = theta + atan(sin(alpha) * tanh(c * s) / c) + PI;" + " }" + " else beta = theta + atan(sin(alpha) * tanh(c * s) / c);" + " }" + "alpha = alpha * sgn; beta = beta * sgn;" + "return vec4(s * cos(beta) * cos(alpha), s * sin(beta) * cos(alpha), s * sin(alpha), 1);" + "}"; + + EX transmatrix adjmatrix(int i, int j) { + ld zs = 2 * M_PI / 28; + if(i == 7) return zpush(-zs) * spin(-2*zs); + if(i == 8) return zpush(+zs) * spin(+2*zs); + return spin(2 * M_PI * i / 7) * xpush(tessf7/2) * spin(M_PI - 2 * M_PI * j / 7); + } + + struct hrmap_psl2 : hrmap { + + hrmap *underlying_map; + + map, cell*> at; + map> where; + + heptagon *getOrigin() override { return underlying_map->getOrigin(); } + + template auto in_underlying(const T& t) -> decltype(t()) { + dynamicval g(geometry, gNormal); + dynamicval gu(currentmap, underlying_map); + return t(); + } + + cell *getCell(cell *u, int h) { + h = gmod(h, 14); + cell*& c = at[make_pair(u, h)]; + if(!c) { c = newCell(u->type+2, u->master); where[c] = {u, h}; } + return c; + } + + cell* gamestart() override { return getCell(underlying_map->gamestart(), 0); } + + virtual transmatrix relative_matrix(cell *c2, cell *c1, const struct hyperpoint& point_hint) override { + for(int i=0; itype; i++) if(c1->move(i) == c2) return adjmatrix(i, c1->c.spin(i)); + if(gmatrix0.count(c2) && gmatrix0.count(c1)) + return inverse(gmatrix0[c1]) * gmatrix0[c2]; + return Id; // not implemented yet + } + + void draw() override { + set visited; + + cell* start = viewcenter(); + vector> dq; + + visited.insert(start); + dq.emplace_back(start, cview()); + + for(int i=0; icmove(i); + if(visited.count(c1)) continue; + visited.insert(c1); + dq.emplace_back(c1, V * adjmatrix(i, c->c.spin(i))); + } + } + } + + hrmap_psl2() { + in_underlying([this] { initcells(); underlying_map = currentmap; }); + for(hrmap*& m: allmaps) if(m == underlying_map) m = NULL; + } + + ~hrmap_psl2() { + in_underlying([this] { delete currentmap; }); + for(auto& p: at) tailored_delete(p.second); + } + + }; + + EX cell *get_at(cell *base, int level) { + return ((hrmap_psl2*)currentmap)->getCell(base, level); + } + + EX pair get_where(cell *c) { return ((hrmap_psl2*)currentmap)->where[c]; } + + void find_cell_connection(cell *c, int d) { + auto m = (hrmap_psl2*)currentmap; + if(d >= c->type - 2) { + cell *c1 = get_at(m->where[c].first, m->where[c].second + (d == c->type-1 ? 1 : -1)); + 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); + cell *c1 = get_at(cu1, m->where[c].second - d1*2 + d*2 + 7); + c->c.connect(d, c1, d1, false); + } + } + +EX } + EX namespace nisot { EX hyperpoint christoffel(const hyperpoint at, const hyperpoint velocity, const hyperpoint transported) { if(sol) return solv::christoffel(at, velocity, transported); else if(nil) return nilv::christoffel(at, velocity, transported); + else if(sl2) return slr::christoffel(at, velocity, transported); else return point3(0, 0, 0); } @@ -789,6 +1139,7 @@ EX namespace nisot { EX hyperpoint inverse_exp(const hyperpoint h, iePrecision p, bool just_direction IS(true)) { if(sol) return solv::get_inverse_exp(h, p == iLazy, just_direction); if(nil) return nilv::get_inverse_exp(h, p == iLazy ? 5 : 20); + if(sl2) return slr::get_inverse_exp(h); if(prod) return product::inverse_exp(h); return point3(h[0], h[1], h[2]); } @@ -822,6 +1173,7 @@ EX namespace nisot { EX hyperpoint get_exp(hyperpoint v, int steps) { if(sol) return direct_exp(v, steps); if(nil) return nilv::formula_exp(v); + if(sl2) return slr::formula_exp(v); if(prod) return product::direct_exp(v); return v; } @@ -831,19 +1183,35 @@ EX namespace nisot { hyperpoint h = tC0(T); h[3] = 0; - h = Pos * h; + auto tPos = transpose(Pos); + const ld eps = 1e-4; + + if(sl2) { + hyperpoint p = slr::to_phigans(tPos[3]); + for(int i=0; i<3; i++) + tPos[i] = (slr::to_phigans(tPos[3] + tPos[i] * eps) - p) / eps; + tPos[3] = p; + h = transpose(tPos) * h; + } + else h = Pos * h; + int steps = 100; h /= steps; - - auto tPos = transpose(Pos); - + for(int i=0; i vertices, vector set_column(T, 1, vertices[1]); set_column(T, 2, vertices[2]); set_column(T, 3, C0); - if(det(T) < 0) + if(det(T) < 0 && !sl2) reverse(vertices.begin(), vertices.end()), reverse(weights.begin(), weights.end()); @@ -716,6 +716,8 @@ void geometry_information::make_wall(int id, vector vertices, vector hyperpoint center = Hypc; int n = isize(vertices); + bool triangles = n > 3 && hypot_d(MDIM, vertices[0] - vertices[3]) < 1e-5; + vector altitudes; if(prod) { altitudes.resize(n); @@ -735,6 +737,7 @@ void geometry_information::make_wall(int id, vector vertices, vector if(prod) for(int i=0; i vertices, vector if(true) { int STEP = vid.texture_step; for(int a=0; a top, bot; + for(int i=0; i<7; i++) { + bot.push_back(center_d); + bot.push_back(spin(a*i) * left_d); + bot.push_back(spin(a*i) * right_d); + bot.push_back(center_d); + bot.push_back(spin(a*i) * right_d); + bot.push_back(spin(a*(i+1)) * left_d); + + top.push_back(center_u); + top.push_back(spin(a*i) * left_u); + top.push_back(spin(a*i) * right_u); + top.push_back(center_u); + top.push_back(spin(a*i) * right_u); + top.push_back(spin(a*(i+1)) * left_u); + } + make_wall(7, bot); + make_wall(8, top); + } if(geometry == gSol) { ld zstep = -log(2) / 2; diff --git a/rug.cpp b/rug.cpp index 2607fd88..71e6a4bd 100644 --- a/rug.cpp +++ b/rug.cpp @@ -997,7 +997,7 @@ bincode acd_bin(ld x) { bincode get_bincode(hyperpoint h) { switch(ginf[gwhere].cclass) { - case gcEuclid: case gcSol: case gcNil: case gcProduct: + case gcEuclid: case gcSol: case gcNil: case gcProduct: case gcSL2: return acd_bin(h[0]) + acd_bin(h[1]) * sY + acd_bin(h[2]) * sZ; case gcHyperbolic: return acd_bin(hypot_d(3, h)); diff --git a/shaders.cpp b/shaders.cpp index 1bcf4b69..82b84bb9 100644 --- a/shaders.cpp +++ b/shaders.cpp @@ -53,7 +53,7 @@ glvertex pointtogl(const hyperpoint& t); enum class shader_projection { standard, band, halfplane, standardH3, standardR3, standardS30, standardS31, standardS32, standardS33, ball, halfplane3, band3, flatten, standardSolv, standardNil, - standardEH2, + standardEH2, standardSL2, MAX }; @@ -635,6 +635,7 @@ void init() { bool sh3 = (sp == shader_projection::standardH3); bool ssol = (sp == shader_projection::standardSolv); bool snil = (sp == shader_projection::standardNil); + bool ssl2 = (sp == shader_projection::standardSL2); bool sr3 = (sp == shader_projection::standardR3); bool ss30 = (sp == shader_projection::standardS30); bool ss31 = (sp == shader_projection::standardS31); @@ -643,7 +644,7 @@ void init() { bool seh2 = (sp == shader_projection::standardEH2); bool ss3 = ss30 || ss31 || ss32 || ss33; - bool s3 = (sh3 || sr3 || ss3 || ssol || snil || seh2); + bool s3 = (sh3 || sr3 || ss3 || ssol || snil || seh2 || ssl2); bool dim3 = s3 || among(sp, shader_projection::ball, shader_projection::halfplane3, shader_projection::band3); bool dim2 = !dim3; bool ball = (sp == shader_projection::ball); @@ -697,6 +698,7 @@ void init() { ssol, solv::solshader, snil, nilv::nilshader, + ssl2, slr::slshader, 1, "void main() {", texture, "vTexCoord = aTexture;", @@ -736,6 +738,7 @@ void init() { ssol, "float ad = (d == 0.) ? 0. : (d < 1.) ? min(atanh(d), 10.) : 10.; ", ssol, "float m = ad / d / 11.; t[0] *= m; t[1] *= m; t[2] *= m; ", snil, "t = inverse_exp(t);", + ssl2, "t = inverse_exp(t);", seh2, "float z = log(t[2] * t[2] - t[0] * t[0] - t[1] * t[1]) / 2.;", seh2, "float r = sqrt(t[0] * t[0] + t[1] * t[1]);", @@ -747,7 +750,7 @@ void init() { seh2, "t[2] = z;", sh3, "float fogs = (uFogBase - acosh(t[3]) / uFog);", - sr3||snil, "float fogs = (uFogBase - sqrt(t[0]*t[0] + t[1]*t[1] + t[2]*t[2]) / uFog);", + sr3||snil||ssl2, "float fogs = (uFogBase - sqrt(t[0]*t[0] + t[1]*t[1] + t[2]*t[2]) / uFog);", ssol, "float fogs = (uFogBase - ad / uFog);", seh2, "float fogs = (uFogBase - sqrt(z*z+d*d) / uFog);", diff --git a/system.cpp b/system.cpp index 6f22e850..7d225c94 100644 --- a/system.cpp +++ b/system.cpp @@ -1187,7 +1187,7 @@ EX void set_geometry(eGeometry target) { if(GDIM == 3 && old_DIM == 2 && pmodel == mdDisk) pmodel = mdPerspective; if(nonisotropic && old_DIM == 2) pmodel = mdGeodesic; if(GDIM == 2 && among(pmodel, mdPerspective, mdGeodesic)) pmodel = mdDisk; - if(nonisotropic && old_DIM == 2 && vid.texture_step < 4) vid.texture_step = 4; + if(nonisotropic && old_DIM == 2 && vid.texture_step < 4 && !sl2) vid.texture_step = 4; if(prod) { pmodel = mdPerspective; if(vid.texture_step < 4) vid.texture_step = 4; } if(prod && shmup::on) shmup::on = false; } @@ -1196,7 +1196,7 @@ EX void set_geometry(eGeometry target) { EX void set_variation(eVariation target) { if(variation != target) { stop_game(); - if(euclid6 || binarytiling || sol || penrose) geometry = gNormal; + if(euclid6 || binarytiling || sol || penrose || WDIM == 3) if(!prod) geometry = gNormal; auto& cd = ginf[gCrystal]; if(target == eVariation::bitruncated && cryst && cd.sides == 8 && cd.vertex == 4) { cd.vertex = 3;